Merge tag 'release_3_0_10' into tr-integ
authorSam Hartman <hartmans@debian.org>
Mon, 16 Nov 2015 15:11:12 +0000 (10:11 -0500)
committerSam Hartman <hartmans@debian.org>
Mon, 16 Nov 2015 15:11:12 +0000 (10:11 -0500)
Conflicts:
raddb/mods-config/attr_filter/access_reject
src/main/tls.c
src/main/tls_listen.c
src/modules/rlm_realm/rlm_realm.c
src/modules/rlm_realm/trustrouter.c

636 files changed:
.gitignore
.travis.yml
CONTRIBUTING [new file with mode: 0644]
Makefile
README.rst
VERSION
acinclude.m4
configure
configure.ac
debian/.gitignore
debian/changelog
debian/compat
debian/control
debian/copyright
debian/freeradius-common.lintian-overrides [new file with mode: 0644]
debian/freeradius-config.lintian-overrides
debian/freeradius-dhcp.install [new file with mode: 0644]
debian/freeradius-dhcp.lintian-overrides [new file with mode: 0644]
debian/freeradius-dhcp.postinst [new file with mode: 0755]
debian/freeradius-iodbc.lintian-overrides
debian/freeradius-krb5.lintian-overrides
debian/freeradius-ldap.lintian-overrides
debian/freeradius-memcached.install [new file with mode: 0644]
debian/freeradius-memcached.lintian-overrides [new file with mode: 0644]
debian/freeradius-memcached.postinst [new file with mode: 0755]
debian/freeradius-mysql.lintian-overrides [new file with mode: 0644]
debian/freeradius-postgresql.lintian-overrides
debian/freeradius-redis.install [new file with mode: 0644]
debian/freeradius-redis.lintian-overrides [new file with mode: 0644]
debian/freeradius-redis.postinst [new file with mode: 0755]
debian/freeradius-rest.lintian-overrides [new file with mode: 0644]
debian/freeradius-utils.install
debian/freeradius-utils.lintian-overrides [deleted file]
debian/freeradius-yubikey.install [new file with mode: 0644]
debian/freeradius-yubikey.lintian-overrides [new file with mode: 0644]
debian/freeradius-yubikey.postinst [new file with mode: 0755]
debian/freeradius.init
debian/freeradius.install
debian/freeradius.lintian-overrides
debian/libfreeradius3.lintian-overrides
debian/patches/disable-dhcp-bydefault.diff [new file with mode: 0644]
debian/patches/logrotate-path.diff [new file with mode: 0644]
debian/patches/radiusd-to-freeradius.diff
debian/patches/series
debian/rules
debian/upstream/signing-key.asc [new file with mode: 0644]
debian/watch [new file with mode: 0644]
doc/ChangeLog
doc/README
doc/bugs
doc/developer/coding-methods.rst
doc/developer/contributing.rst
doc/developer/module_interface.rst
doc/developer/release-method.rst
doc/modules/rlm_eap
doc/modules/rlm_sqlippool
doc/rfc/rfc7055.txt [new file with mode: 0644]
doc/rfc/rfc7268.txt [new file with mode: 0644]
doc/rfc/rfc7542.txt [new file with mode: 0644]
doc/rfc/rfc7599.txt [new file with mode: 0644]
doc/schemas/ldap/iplanet/freeradius.ldif
doc/schemas/ldap/iplanet/freeradius.schema
doc/schemas/ldap/openldap/freeradius.ldif
doc/schemas/ldap/openldap/freeradius.schema
doc/schemas/logstash/README [new file with mode: 0644]
doc/schemas/logstash/radius-mapping.sh [new file with mode: 0644]
doc/schemas/logstash/radius.conf [new file with mode: 0644]
doc/source/Doxyfile
doc/source/extra/module.c
doc/source/extra/toc.c
m4/ax_cc.m4
man/man1/radclient.1
man/man1/radwho.1
man/man5/checkrad.5
man/man5/dictionary.5
man/man5/radrelay.conf.5
man/man5/rlm_attr_filter.5
man/man5/rlm_detail.5
man/man5/rlm_digest.5
man/man5/rlm_passwd.5
man/man5/unlang.5
man/man5/users.5
man/man8/radcrypt.8
man/man8/radiusd.8
man/man8/radmin.8
man/man8/radrelay.8
man/man8/radsniff.8
man/man8/radsqlrelay.8
mibs/FREERADIUS-MGMT-MIB.mib [moved from mibs/FREERADIUS-MGMT-MIB.txt with 100% similarity]
mibs/FREERADIUS-NOTIFICATION-MIB.mib [moved from mibs/FREERADIUS-NOTIFICATION-MIB.txt with 100% similarity]
mibs/FREERADIUS-PRODUCT-RADIUSD-MIB.mib [moved from mibs/FREERADIUS-PRODUCT-RADIUSD-MIB.txt with 100% similarity]
mibs/FREERADIUS-SMI.mib [moved from mibs/FREERADIUS-SMI.txt with 100% similarity]
mibs/RADIUS-ACC-CLIENT-MIB.mib [moved from mibs/RADIUS-ACC-CLIENT-MIB.txt with 100% similarity]
mibs/RADIUS-ACC-SERVER-MIB.chart [deleted file]
mibs/RADIUS-ACC-SERVER-MIB.mib [moved from mibs/RADIUS-ACC-SERVER-MIB.txt with 100% similarity]
mibs/RADIUS-AUTH-CLIENT-MIB.mib [moved from mibs/RADIUS-AUTH-CLIENT-MIB.txt with 100% similarity]
mibs/RADIUS-AUTH-SERVER-MIB.chart [deleted file]
mibs/RADIUS-AUTH-SERVER-MIB.mib [moved from mibs/RADIUS-AUTH-SERVER-MIB.txt with 100% similarity]
mibs/RADIUS-STAT-MIB.mib [moved from mibs/RADIUS-STAT-MIB.txt with 100% similarity]
mibs/README
raddb/certs/Makefile
raddb/certs/ca.cnf
raddb/certs/client.cnf
raddb/certs/server.cnf
raddb/mods-available/cache
raddb/mods-available/cache_eap
raddb/mods-available/couchbase
raddb/mods-available/detail
raddb/mods-available/dhcp_sqlippool
raddb/mods-available/eap
raddb/mods-available/exec
raddb/mods-available/expr
raddb/mods-available/files
raddb/mods-available/inner-eap
raddb/mods-available/krb5
raddb/mods-available/ldap
raddb/mods-available/linelog
raddb/mods-available/mschap
raddb/mods-available/opendirectory
raddb/mods-available/redis
raddb/mods-available/rest
raddb/mods-available/sql
raddb/mods-available/sqlcounter
raddb/mods-available/yubikey
raddb/mods-config/attr_filter/access_reject
raddb/mods-config/attr_filter/post-proxy
raddb/mods-config/perl/example.pl
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/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/sqlite/dailycounter.conf
raddb/mods-config/sql/counter/sqlite/monthlycounter.conf
raddb/mods-config/sql/main/mssql/queries.conf
raddb/mods-config/sql/main/mysql/queries.conf
raddb/mods-config/sql/main/oracle/queries.conf
raddb/mods-config/sql/main/postgresql/queries.conf
raddb/mods-config/sql/main/postgresql/setup.sql
raddb/mods-config/sql/main/sqlite/queries.conf
raddb/policy.d/abfab-tr
raddb/policy.d/accounting
raddb/policy.d/canonicalization
raddb/policy.d/debug
raddb/proxy.conf
raddb/radiusd.conf.in
raddb/sites-available/abfab-tr-idp
raddb/sites-available/challenge [new file with mode: 0644]
raddb/sites-available/coa
raddb/sites-available/default
raddb/sites-available/dhcp
raddb/sites-available/inner-tunnel
raddb/sites-available/robust-proxy-accounting
raddb/sites-available/tls
raddb/vmpsd.conf.in
redhat/freeradius-radiusd-init
redhat/freeradius.spec
redhat/radiusd.service [new file with mode: 0644]
scripts/exec-program-wait
scripts/git/post-receive
scripts/install.mk
scripts/libtool.mk
scripts/raduat
share/dictionary
share/dictionary.3gpp2
share/dictionary.alcatel.sr
share/dictionary.aptilo [new file with mode: 0644]
share/dictionary.arbor
share/dictionary.arista [new file with mode: 0644]
share/dictionary.aruba
share/dictionary.boingo [new file with mode: 0644]
share/dictionary.cisco.asa
share/dictionary.dhcp
share/dictionary.ericsson.ab
share/dictionary.freeradius.internal
share/dictionary.microsoft
share/dictionary.mikrotik
share/dictionary.motorola.wimax
share/dictionary.rfc7155
share/dictionary.rfc7499 [new file with mode: 0644]
share/dictionary.wifialliance [new file with mode: 0644]
share/format.pl
src/include/.gitignore
src/include/all.mk
src/include/autoconf.h.in
src/include/base64.h
src/include/build.h
src/include/clients.h [new file with mode: 0644]
src/include/conffile.h
src/include/connection.h
src/include/detail.h
src/include/dhcp.h
src/include/exfile.h
src/include/libradius.h
src/include/listen.h [new file with mode: 0644]
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/net.h
src/include/packet.h
src/include/parser.h
src/include/pcap.h
src/include/process.h
src/include/radclient.h
src/include/radius.h
src/include/radiusd.h
src/include/radsniff.h
src/include/regex.h
src/include/sha1.h
src/include/state.h
src/include/tcp.h
src/include/threads.h
src/include/tls-h
src/include/tmpl.h
src/include/xlat.h
src/lib/all.mk
src/lib/base64.c
src/lib/cursor.c
src/lib/debug.c
src/lib/dict.c
src/lib/event.c
src/lib/fifo.c
src/lib/filters.c
src/lib/getaddrinfo.c
src/lib/hash.c
src/lib/hmacmd5.c
src/lib/hmacsha1.c
src/lib/log.c
src/lib/md4.c
src/lib/md5.c
src/lib/misc.c
src/lib/missing.c
src/lib/net.c
src/lib/packet.c
src/lib/pair.c
src/lib/pcap.c
src/lib/print.c
src/lib/radius.c
src/lib/regex.c
src/lib/sha1.c
src/lib/socket.c [new file with mode: 0644]
src/lib/tcp.c
src/lib/token.c
src/lib/udpfromto.c
src/lib/value.c
src/main/acct.c
src/main/auth.c
src/main/cb.c
src/main/checkrad.in
src/main/client.c
src/main/command.c
src/main/conffile.c
src/main/connection.c
src/main/detail.c
src/main/evaluate.c
src/main/exec.c
src/main/exfile.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/pair.c
src/main/parser.c
src/main/process.c
src/main/radattr.c
src/main/radattr.mk
src/main/radclient.c
src/main/radiusd.c
src/main/radmin.c
src/main/radsniff.c
src/main/radwho.c
src/main/radzap
src/main/realms.c
src/main/regex.c
src/main/session.c
src/main/soh.c
src/main/state.c
src/main/stats.c
src/main/threads.c
src/main/tls.c
src/main/tls_listen.c
src/main/tmpl.c
src/main/unittest.c
src/main/unittest.mk
src/main/util.c
src/main/version.c
src/main/xlat.c
src/modules/proto_dhcp/all.mk
src/modules/proto_dhcp/dhcp.c
src/modules/proto_dhcp/dhcpclient.c
src/modules/proto_dhcp/dhcpd.c
src/modules/proto_dhcp/libfreeradius-dhcp.mk [new file with mode: 0644]
src/modules/proto_dhcp/proto_dhcp.mk
src/modules/proto_dhcp/rlm_dhcp.c
src/modules/proto_dhcp/rlm_dhcp.mk
src/modules/proto_vmps/vmps.c
src/modules/proto_vmps/vqp.c
src/modules/proto_vmps/vqpcli.pl [new file with mode: 0755]
src/modules/rlm_always/rlm_always.c
src/modules/rlm_attr_filter/rlm_attr_filter.c
src/modules/rlm_cache/drivers/rlm_cache_memcached/configure
src/modules/rlm_cache/drivers/rlm_cache_memcached/configure.ac
src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c
src/modules/rlm_cache/drivers/rlm_cache_rbtree/rlm_cache_rbtree.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_cache/rlm_cache.h
src/modules/rlm_cache/serialize.c
src/modules/rlm_chap/rlm_chap.c
src/modules/rlm_couchbase/README.md
src/modules/rlm_couchbase/config.h.in
src/modules/rlm_couchbase/configure
src/modules/rlm_couchbase/configure.ac
src/modules/rlm_couchbase/couchbase.c
src/modules/rlm_couchbase/couchbase.h
src/modules/rlm_couchbase/jsonc_missing.c
src/modules/rlm_couchbase/jsonc_missing.h
src/modules/rlm_couchbase/mod.c
src/modules/rlm_couchbase/mod.h
src/modules/rlm_couchbase/rlm_couchbase.c
src/modules/rlm_counter/rad_counter
src/modules/rlm_counter/rlm_counter.c
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/eap.c
src/modules/rlm_eap/eap.h
src/modules/rlm_eap/libeap/eap_chbind.c
src/modules/rlm_eap/libeap/eap_tls.c
src/modules/rlm_eap/libeap/eap_tls.h
src/modules/rlm_eap/libeap/eap_types.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/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/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.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_md5/eap_md5.c
src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.h
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/peap.c
src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c
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/rlm_eap_sim.c
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c
src/modules/rlm_eap/types/rlm_eap_tnc/rlm_eap_tnc.c
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/rlm_example.c
src/modules/rlm_exec/rlm_exec.c
src/modules/rlm_expiration/rlm_expiration.c
src/modules/rlm_expr/paircmp.c
src/modules/rlm_expr/rlm_expr.c
src/modules/rlm_files/rlm_files.c
src/modules/rlm_idn/rlm_idn.c
src/modules/rlm_ippool/rlm_ippool.c
src/modules/rlm_krb5/krb5.c
src/modules/rlm_krb5/rlm_krb5.c
src/modules/rlm_ldap/all.mk.in
src/modules/rlm_ldap/attrmap.c
src/modules/rlm_ldap/clients.c
src/modules/rlm_ldap/config.h.in
src/modules/rlm_ldap/configure
src/modules/rlm_ldap/configure.ac
src/modules/rlm_ldap/edir.c
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_ldap/sasl.c [new file with mode: 0644]
src/modules/rlm_linelog/rlm_linelog.c
src/modules/rlm_logintime/rlm_logintime.c
src/modules/rlm_logintime/timestr.c
src/modules/rlm_mschap/auth_wbclient.c [new file with mode: 0644]
src/modules/rlm_mschap/auth_wbclient.h [new file with mode: 0644]
src/modules/rlm_mschap/config.h.in
src/modules/rlm_mschap/configure
src/modules/rlm_mschap/configure.ac
src/modules/rlm_mschap/mschap.c
src/modules/rlm_mschap/opendir.c
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_mschap/rlm_mschap.h [new file with mode: 0644]
src/modules/rlm_opendirectory/rlm_opendirectory.c
src/modules/rlm_otp/otp_mppe.c
src/modules/rlm_otp/otp_pw_valid.c
src/modules/rlm_otp/otp_pwe.c
src/modules/rlm_otp/otp_radstate.c
src/modules/rlm_otp/rlm_otp.c
src/modules/rlm_pam/configure.ac
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/rlm_perl.c
src/modules/rlm_preprocess/rlm_preprocess.c
src/modules/rlm_python/rlm_python.c
src/modules/rlm_radutmp/rlm_radutmp.c
src/modules/rlm_realm/rlm_realm.c
src/modules/rlm_realm/trustrouter.c
src/modules/rlm_redis/.gitignore
src/modules/rlm_redis/rlm_redis.c
src/modules/rlm_redis/rlm_redis.h
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/rest.c
src/modules/rlm_rest/rest.h
src/modules/rlm_rest/rlm_rest.c
src/modules/rlm_ruby/rlm_ruby.c
src/modules/rlm_securid/configure
src/modules/rlm_securid/configure.ac
src/modules/rlm_securid/mem.c
src/modules/rlm_securid/rlm_securid.c
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/drivers/rlm_sql_db2/rlm_sql_db2.c
src/modules/rlm_sql/drivers/rlm_sql_firebird/rlm_sql_firebird.c
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_iodbc/configure.ac
src/modules/rlm_sql/drivers/rlm_sql_iodbc/rlm_sql_iodbc.c
src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c
src/modules/rlm_sql/drivers/rlm_sql_null/rlm_sql_null.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/rlm_sql_postgresql.c
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/configure.ac
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/rlm_sqlcounter.c
src/modules/rlm_sqlhpwippool/all.mk [new file with mode: 0644]
src/modules/rlm_sqlhpwippool/rlm_sqlhpwippool.c
src/modules/rlm_sqlippool/all.mk [new file with mode: 0644]
src/modules/rlm_sqlippool/rlm_sqlippool.c
src/modules/rlm_test/rlm_test.c
src/modules/rlm_unbound/rlm_unbound.c
src/modules/rlm_unix/rlm_unix.c
src/modules/rlm_unpack/rlm_unpack.c
src/modules/rlm_utf8/rlm_utf8.c
src/modules/rlm_wimax/rlm_wimax.c
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/all.mk
src/tests/auth/all.mk
src/tests/bob [new file with mode: 0644]
src/tests/eap-md5.conf
src/tests/eap-tls.conf
src/tests/eap-ttls-eap-mschapv2.conf
src/tests/eap-ttls-pap.conf
src/tests/eapsim-03/radiusd-example.txt
src/tests/keywords/all.mk
src/tests/keywords/case-empty [new file with mode: 0644]
src/tests/keywords/count-error [new file with mode: 0644]
src/tests/keywords/else-error [new file with mode: 0644]
src/tests/keywords/expr
src/tests/keywords/foreach-break-2
src/tests/keywords/foreach-break-3 [new file with mode: 0644]
src/tests/keywords/foreach-break-4 [new file with mode: 0644]
src/tests/keywords/if-multivalue
src/tests/keywords/if-regex-match-comp
src/tests/keywords/if-regex-multivalue [new file with mode: 0644]
src/tests/keywords/map-xlat [new file with mode: 0644]
src/tests/keywords/pairs
src/tests/keywords/radiusd.conf
src/tests/keywords/redundant-redundant [new file with mode: 0644]
src/tests/keywords/switch-escape [new file with mode: 0644]
src/tests/keywords/switch-virtual [new file with mode: 0644]
src/tests/keywords/update-delete [new file with mode: 0644]
src/tests/keywords/update-index [new file with mode: 0644]
src/tests/keywords/virtual [new file with mode: 0644]
src/tests/keywords/virtual-exists [new file with mode: 0644]
src/tests/keywords/virtual-load-balance [new file with mode: 0644]
src/tests/keywords/virtual-rhs [new file with mode: 0644]
src/tests/keywords/virtual_policy [new file with mode: 0644]
src/tests/keywords/with_dots [new file with mode: 0644]
src/tests/keywords/xlat-list [new file with mode: 0644]
src/tests/map/all.mk [new file with mode: 0644]
src/tests/map/base [new file with mode: 0644]
src/tests/map/base.out [new file with mode: 0644]
src/tests/map/count-error [new file with mode: 0644]
src/tests/map/count-list-error [new file with mode: 0644]
src/tests/map/map_tests.mk [new file with mode: 0644]
src/tests/map/map_unit.c [new file with mode: 0644]
src/tests/map/map_unit.mk [new file with mode: 0644]
src/tests/modules/README.rst
src/tests/modules/all.mk
src/tests/modules/always/all.mk
src/tests/modules/cache/rbtree/all.mk
src/tests/modules/files/all.mk
src/tests/modules/ldap/acct.attrs [new file with mode: 0644]
src/tests/modules/ldap/acct.unlang [new file with mode: 0644]
src/tests/modules/ldap/all.mk [new file with mode: 0644]
src/tests/modules/ldap/auth.attrs [new file with mode: 0644]
src/tests/modules/ldap/auth.unlang [new file with mode: 0644]
src/tests/modules/ldap/example.com.ldif [new symlink]
src/tests/modules/ldap/groups_rfc2307bis.attrs [new file with mode: 0644]
src/tests/modules/ldap/groups_rfc2307bis.unlang [new file with mode: 0644]
src/tests/modules/ldap/module.conf [new file with mode: 0644]
src/tests/modules/preprocess/all.mk [new file with mode: 0644]
src/tests/modules/preprocess/hints [new file with mode: 0644]
src/tests/modules/preprocess/huntgroups [new file with mode: 0644]
src/tests/modules/preprocess/module.conf [new file with mode: 0644]
src/tests/modules/preprocess/xlat.attrs [new file with mode: 0644]
src/tests/modules/preprocess/xlat.unlang [new file with mode: 0644]
src/tests/modules/radiusd.conf
src/tests/modules/sql/.gitignore [new file with mode: 0644]
src/tests/modules/sql/README [new file with mode: 0644]
src/tests/modules/sql/acct_0_start.attrs [new file with mode: 0644]
src/tests/modules/sql/acct_0_start.unlang [new file with mode: 0644]
src/tests/modules/sql/acct_1_update.attrs [new file with mode: 0644]
src/tests/modules/sql/acct_1_update.unlang [new file with mode: 0644]
src/tests/modules/sql/acct_2_stop.attrs [new file with mode: 0644]
src/tests/modules/sql/acct_2_stop.unlang [new file with mode: 0644]
src/tests/modules/sql/acct_start_conflict.attrs [new file with mode: 0644]
src/tests/modules/sql/acct_start_conflict.unlang [new file with mode: 0644]
src/tests/modules/sql/acct_update_no_start.attrs [new file with mode: 0644]
src/tests/modules/sql/acct_update_no_start.unlang [new file with mode: 0644]
src/tests/modules/sql/auth.attrs [new file with mode: 0644]
src/tests/modules/sql/auth.unlang [new file with mode: 0644]
src/tests/modules/sql/reject.attrs [new file with mode: 0644]
src/tests/modules/sql/reject.unlang [new file with mode: 0644]
src/tests/modules/sql_mysql/.gitignore [new file with mode: 0644]
src/tests/modules/sql_mysql/acct_0_start.attrs [new symlink]
src/tests/modules/sql_mysql/acct_0_start.unlang [new symlink]
src/tests/modules/sql_mysql/acct_1_update.attrs [new symlink]
src/tests/modules/sql_mysql/acct_1_update.unlang [new symlink]
src/tests/modules/sql_mysql/acct_2_stop.attrs [new symlink]
src/tests/modules/sql_mysql/acct_2_stop.unlang [new symlink]
src/tests/modules/sql_mysql/acct_start_conflict.attrs [new symlink]
src/tests/modules/sql_mysql/acct_start_conflict.unlang [new symlink]
src/tests/modules/sql_mysql/acct_update_no_start.attrs [new symlink]
src/tests/modules/sql_mysql/acct_update_no_start.unlang [new symlink]
src/tests/modules/sql_mysql/all.mk [new file with mode: 0644]
src/tests/modules/sql_mysql/auth.attrs [new symlink]
src/tests/modules/sql_mysql/auth.unlang [new symlink]
src/tests/modules/sql_mysql/module.conf [new file with mode: 0644]
src/tests/modules/sql_mysql/reject.attrs [new symlink]
src/tests/modules/sql_mysql/reject.unlang [new symlink]
src/tests/modules/sql_postgresql/.gitignore [new file with mode: 0644]
src/tests/modules/sql_postgresql/acct_0_start.attrs [new symlink]
src/tests/modules/sql_postgresql/acct_0_start.unlang [new symlink]
src/tests/modules/sql_postgresql/acct_1_update.attrs [new symlink]
src/tests/modules/sql_postgresql/acct_1_update.unlang [new symlink]
src/tests/modules/sql_postgresql/acct_2_stop.attrs [new symlink]
src/tests/modules/sql_postgresql/acct_2_stop.unlang [new symlink]
src/tests/modules/sql_postgresql/acct_start_conflict.attrs [new symlink]
src/tests/modules/sql_postgresql/acct_start_conflict.unlang [new symlink]
src/tests/modules/sql_postgresql/acct_update_no_start.attrs [new symlink]
src/tests/modules/sql_postgresql/acct_update_no_start.unlang [new symlink]
src/tests/modules/sql_postgresql/all.mk [new file with mode: 0644]
src/tests/modules/sql_postgresql/auth.attrs [new symlink]
src/tests/modules/sql_postgresql/auth.unlang [new symlink]
src/tests/modules/sql_postgresql/module.conf [new file with mode: 0644]
src/tests/modules/sql_postgresql/reject.attrs [new symlink]
src/tests/modules/sql_postgresql/reject.unlang [new symlink]
src/tests/modules/sql_sqlite/acct_0_start.attrs [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_0_start.unlang [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_1_update.attrs [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_1_update.unlang [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_2_stop.attrs [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_2_stop.unlang [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_start_conflict.attrs [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_start_conflict.unlang [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_update_no_start.attrs [changed from file to symlink]
src/tests/modules/sql_sqlite/acct_update_no_start.unlang [changed from file to symlink]
src/tests/modules/sql_sqlite/all.mk
src/tests/modules/sql_sqlite/auth.attrs [new symlink]
src/tests/modules/sql_sqlite/auth.unlang [new symlink]
src/tests/modules/sql_sqlite/module.conf
src/tests/modules/sql_sqlite/reject.attrs [new symlink]
src/tests/modules/sql_sqlite/reject.unlang [new symlink]
src/tests/modules/test.mk [new file with mode: 0644]
src/tests/peap-client-mschapv2.conf [new file with mode: 0644]
src/tests/rbmonkey.c
src/tests/salt-test-server/.gitignore [new file with mode: 0644]
src/tests/salt-test-server/README [new file with mode: 0644]
src/tests/salt-test-server/build.sh [new file with mode: 0755]
src/tests/salt-test-server/salt/iptable.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/iptables [new file with mode: 0644]
src/tests/salt-test-server/salt/ldap.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/ldap/base.ldif [new file with mode: 0644]
src/tests/salt-test-server/salt/ldap/schema_freeradius.ldif [new file with mode: 0644]
src/tests/salt-test-server/salt/mysql.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/mysql/schema.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/mysql/setup.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/ntp.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/postgres.sls [new file with mode: 0644]
src/tests/salt-test-server/salt/postgres/schema.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/postgres/setup.sql [new file with mode: 0644]
src/tests/salt-test-server/salt/top.sls [new file with mode: 0644]
src/tests/salt-test-server/salt_config/master [new file with mode: 0644]
src/tests/salt-test-server/salt_config/roster [new file with mode: 0644]
src/tests/unit/all.mk
src/tests/unit/condition.txt
src/tests/unit/dhcp.txt [new file with mode: 0644]
src/tests/unit/escape.txt
src/tests/unit/extended.txt
src/tests/unit/rfc.txt
src/tests/unit/tunnel.txt [new file with mode: 0644]
src/tests/unit/vendor.txt
src/tests/unit/wimax.txt
src/tests/unit/xlat.txt
suse/freeradius.spec

index b410ada..f4cb71a 100644 (file)
@@ -32,6 +32,9 @@
 *.project
 *.bbprojectd
 
+# Exuberant ctags
+tags
+
 # Local icon files
 icon.png
 
index 76b58e5..2844efe 100644 (file)
@@ -2,14 +2,16 @@ language: c
 compiler:
   - clang
   - gcc
+sudo: false
 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 BUILD_CFLAGS="-DWITH_EVAL_DEBUG"
-    - LIBS_OPTIONAL=yes LIBS_SHARED=yes BUILD_CFLAGS="-DWITH_EVAL_DEBUG" CLANG_ANALYZE=yes
-    - LIBS_OPTIONAL=yes LIBS_SHARED=yes BUILD_CFLAGS="-DWITH_EVAL_DEBUG -O2 -g3"
+    - DO_BUILD=yes LIBS_OPTIONAL=no LIBS_SHARED=yes BUILD_CFLAGS="-DWITH_EVAL_DEBUG"
+    - DO_BUILD=yes LIBS_OPTIONAL=yes LIBS_SHARED=yes BUILD_CFLAGS="-DWITH_EVAL_DEBUG"
+    - DO_BUILD=yes LIBS_OPTIONAL=yes LIBS_SHARED=yes BUILD_CFLAGS="-DWITH_EVAL_DEBUG -O2 -g3"
+    - DO_BUILD=no
 addons:
   coverity_scan:
     project:
@@ -20,58 +22,68 @@ addons:
     build_command_prepend: ./configure
     build_command: make
     branch_pattern: coverity_scan
+  apt:
+    sources:
+      - couchbase-precise
+    packages:
+      - autoconf
+      - build-essential
+      - debhelper
+      - devscripts
+      - dh-make
+      - doxygen
+      - fakeroot
+      - gdb
+      - graphviz
+      - lintian
+      - pbuilder
+      - python-dev
+      - quilt
+      - libruby
+      - ruby-dev
+      - libcollectdclient-dev
+      - firebird-dev
+      - freetds-dev
+      - libcap-dev
+      - libcouchbase2-libevent
+      - libcouchbase-dev
+      - libcurl4-openssl-dev
+      - libgdbm-dev
+      - libhiredis-dev
+      - libidn11-dev
+      - libiodbc2-dev libiodbc2
+      - libjson0
+      - libjson0-dev
+      - libkrb5-dev
+      - libldap2-dev
+      - libmemcached-dev
+      - libmysqlclient-dev
+      - libpam0g-dev
+      - libpcap-dev
+      - libpcre3-dev
+      - libperl-dev
+      - libpq-dev
+      - libreadline-dev
+      - libsnmp-dev
+      - libssl-dev
+      - libtalloc-dev
+      - libtalloc2-dbg
+      - libunbound-dev
+      - libwbclient-dev
+      - libykclient-dev
+      - libyubikey-dev
 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 --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
-      libcap-dev
-      libcouchbase2-libevent
-      libcouchbase-dev
-      libcurl4-openssl-dev
-      libgdbm-dev
-      libhiredis-dev
-      libidn11-dev
-      libiodbc2-dev libiodbc2
-      libjson0
-      libjson0-dev
-      libkrb5-dev
-      libldap2-dev
-      libmemcached-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
 before_script:
-  - 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 --with-pcre=$LIBS_OPTIONAL
-  - make -j4
-  - if [ "${CLANG_ANALYZE}" = 'yes' -a ${CC} = 'clang' ]; then make -j4 scan; fi
-script: if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then make travis-test; fi
+# Configure the server
+  - if [ "${DO_BUILD}" = 'yes' ]; then 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 --with-pcre=$LIBS_OPTIONAL; fi
+  - if [ "${DO_BUILD}" = 'no' ]; then ./configure -C --without-modules; fi
+# Build the server
+  - if [ "${DO_BUILD}" = 'yes' ]; then make -j8; fi
+# Run CLANG analyzer if we're building with CLANG
+  - if [ "${DO_BUILD}" = 'yes' -a "${COVERITY_SCAN_BRANCH}" != 1 -a ${CC} = 'clang' ]; then make -j8 scan && [ "$(find build/plist/ -name *.html)" = '' ]; fi
+script:
+  - if [ "${DO_BUILD}" = 'yes' -a "${COVERITY_SCAN_BRANCH}" != 1 ]; then make travis-test; fi
+#  - if [ "${DO_BUILD}" = 'no' ]; then make deb; fi
+# Build the doxygen documentation
+  - if [ "${DO_BUILD}" = 'no' ]; then cd doc/source; doxygen 3>&1 1>&2 2>&3 | grep -iv '^warning:' | tee doxygen_stderr.log && [ ! -n "$(cat doxygen_stderr.log)" ]; fi
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644 (file)
index 0000000..7deb291
--- /dev/null
@@ -0,0 +1,109 @@
+0.INTRODUCTION
+
+  The FreeRADIUS project wouldn't exist without contributions from a significant number of developers.
+
+  We greatly value all comments, defect reports, patches/pull-requests, but must balance individual
+  contributor's desires and practices against what's required for the project to operate efficiently.
+
+  This document describes best practices when interacting with members of the FreeRADIUS project team
+  via GitHub.  If you follow these guidelines, it is very likely that your question, bug report or pull
+  request will be acted on, and in a timely manor.
+
+  If you choose to ignore these guidelines our response will be a link to this document.
+
+
+1.GITHUB ISSUE TRACKER
+
+  The GitHub issue tracker is for non-security related defect reports, feature requests, and
+  pull-requests ONLY.
+
+  It is not for support requests or questions regarding configuration/operation of the server, they
+  belong on the users mailing list:
+
+    http://freeradius.org/list/users.html
+
+  Raising support requests or questions as issues will result in them being closed and locked.  If you
+  continue to raise these questions as issues you will be banned from the FreeRADIUS project's GitHub
+  repositories.
+
+  Security issues should be reported to security@freeradius.org, especially if they can be remotely
+  exploited.  This ensures that patches can be developed before the exploit is made public.
+
+
+2.BEFORE REPORTING A DEFECT
+
+  Verify it's still present in the Git HEAD.  Checkout the appropriate branch for the version of the
+  server you're working with as listed here (http://doc.freeradius.org), build the server, and attempt
+  to reproduce your issue.
+
+  The ChangeLog (https://github.com/FreeRADIUS/freeradius-server/blob/v3.0.x/doc/ChangeLog) for the
+  current stable branch may also be used to determine if your issue has already been addressed.
+  The ChangeLog is updated as fixes are made to the server code, and usually reflects the state of the
+  Git HEAD.
+
+  Do not report non-security defects for EOL branches (as listed on doc.freeradius.org), they will be
+  closed and locked.
+
+
+3.CONTENTS OF A DEFECT REPORT
+
+  See doc/bugs (https://github.com/FreeRADIUS/freeradius-server/blob/master/doc/bugs) for information
+  on what to include, and how to obtain it.
+
+  When logging bug reports using the GitHub issue tracker, pay attention to formatting.  You should
+  ensure any log output is surrounded by two sets of tripple backticks (```).  If you don't do this
+  Github will automatically link your issue to other pre-existing issues when it encounters a #<num>
+  string.
+
+
+4.PULL REQUESTS AND CODING STANDARDS
+
+  If you're developing a new feature, module, or writing large amounts of code to fix a defect, contact
+  a member of the FreeRADIUS development team first.  For simpler one or two line fixes, go ahead and
+  open a pull-request immediately.
+
+  The dev team can be contacted via the devel mailing list (http://freeradius.org/list/devel.html),
+  or via GitHub by using the GitHub issue tracker.
+
+  Contacting the dev team gives us the opportunity to offer feedback.  We may have a solution to your
+  problem that doesn't require additional code, or may have ideas as to how your problem can be solved
+  in a way that will better fit with the longterm vision for the server.
+
+  Once you've got the go ahead, read through the coding standards document:
+
+    http://wiki.freeradius.org/contributing/coding-standards
+
+  If you're creating a new module you may wish to read the module creation guide:
+
+    http://wiki.freeradius.org/contributing/Modules3
+
+  You may also wish to utilise the doxygen site to review code documentation:
+
+    http://doc.freeradius.org
+
+  The doxygen site contains the complete reference of all API functions with doxygen headers, as well
+  as structs, and callback declarations.  doc.freeradius.org is updated within one minute of each commit
+  to the master branch of the main freeradius-server repo.
+
+  Finally, this file was written to be displayed automatically on the GitHub issue tracker, so
+  Git/GitHub knowledge is assumed.  If you're wondering what the heck a pull-request is, this
+  document may be of some use:
+
+    http://wiki.freeradius.org/contributing/GitHub
+
+
+5.CONTINUOUS INTEGRATION TESTS
+
+  If possible include test cases in your pull-requests.
+
+  There are currently three test frameworks for different elements of the server:
+
+    Unit tests   - src/tests/unit/*.txt            - Tests for conditions and protocol encoders/decoders.
+    Module tests - src/tests/modules/<module name> - Tests for module functionality.
+    Unlang tests - src/tests/unlang/<test series>  - Tests for unlang keywords and functions.
+
+  See README.* docs in the directories above for basic information on writing test cases.  The easiest
+  way to write new tests is to use the existing tests as examples.
+
+  Tests are run via Travis for each pull-request, and on every commit by a developer with repository
+  access.
index 2beeaee..bc0150f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -64,7 +64,6 @@ travis-test: raddb/test.conf test
        @$(MAKE) install
        @perl -p -i -e 's/allow_vulnerable_openssl = no/allow_vulnerable_openssl = yes/' ${raddbdir}/radiusd.conf
        @${sbindir}/radiusd -XC
-       @$(MAKE) deb
 endif
 
 #
index ca2fad5..45005fa 100644 (file)
@@ -47,6 +47,38 @@ Installation
 
 To install the server, please see the INSTALL file in this directory.
 
+Configuring the server
+----------------------
+
+We understand that the server may be difficult to configure,
+install, or administer.  It is, after all, a complex system with many
+different configuration possibilities.
+
+The most common problem is that people change large amounts of the
+configuration without understanding what they're doing, and without
+testing their changes.  The preferred method of operation is the
+following:
+
+1. Start off with the default configuration files.
+2. Save a copy of the default configuration: It WORKS.  Don't change it!
+3. Verify that the server starts - in debugging mode (``radiusd -X``).
+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).
+  
+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.
+8. Repeat from step (3).
+
+This method will ensure that you have a working configuration that
+is customized to your site as quickly as possible.  While it may seem
+frustrating to proceed via a series of small steps, the alternative
+will always take more time.  The "fast and loose" way will be MORE
+frustrating than quickly making forward progress!
 
 Debugging the Server
 --------------------
@@ -73,56 +105,31 @@ Type some key words into the search box, and you should find
 discussions about common problems and solution.
 
 
-Additional Information
-----------------------
-
-See ``doc/README`` for more information about FreeRADIUS.
-
-There is an O'Reilly book available.  It serves as a good
-introduction for anyone new to RADIUS.  However, it is almost 12 years
-old, and is not much more than a basic introduction to the subject.
-
-http://www.amazon.com/exec/obidos/ASIN/0596003226/freeradiusorg-20/
-
-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
-different configuration possibilities.
-
-The most common problem is that people change large amounts of the
-configuration without understanding what they're doing, and without
-testing their changes.  The preferred method of operation is the
-following:
-
-1. Start off with the default configuration files.
-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).
-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.
-8. Repeat from step (3).
+Feedback, Defects, and Community Support
+----------------------------------------
 
-This method will ensure that you have a working configuration that
-is customized to your site as quickly as possible.  While it may seem
-frustrating to proceed via a series of small steps, the alternative
-will always take more time.  The "fast and loose" way will be MORE
-frustrating than quickly making forward progress!
+If you have any comments, or are having difficulty getting FreeRADIUS
+to do what you want, please post to the 'freeradius-users' list
+(see the URL above).  The FreeRADIUS mailing list is operated and
+contributed to, by the FreeRADIUS community. Users of the list will be
+more than happy to answer your questions, with the caveat that you've
+read documentation relevant to your issue first.
 
+If you suspect a defect in the server, would like to request a feature,
+or submit a code patch, please use the GitHub issue tracker for the
+freeradius-server `repository
+<https://github.com/FreeRADIUS/freeradius-server>`_.
 
-Feedback
---------
+Instructions for gathering data for defect reports can be found in
+``doc/bugs`` or on the `wiki
+<http://wiki.freeradius.org/project/bug-reports>`_.
 
-If you have any comments, bug reports, problems, or concerns, please
-send them to the 'freeradius-users' list (see the URL above).  We will
-do our best to answer your questions, to fix the problems, and to
-generally improve the server in any way we can.
+Under no circumstances should the issue tracker be used for support
+requests, those questions belong on the user's mailing list.  If you
+post questions related to the server in the issue tracker, the issue
+will be closed and locked.  If you persist in positing questions to
+the issue tracker you will be banned from all FreeRADIUS project
+repositories on GitHub.
 
 Please do NOT complain that the developers aren't answering your
 questions quickly enough, or aren't fixing the problems quickly
@@ -131,10 +138,10 @@ documentation.  We recognize that the documentation isn't perfect, but
 it *does* exist, and reading it can solve most common questions.
 
 FreeRADIUS is the cumulative effort of many years of work by many
-people, and you've gotten it for free.  No one gets paid to work on
-FreeRADIUS, and no one is getting paid to answer your questions.  This
-is free software, and the only way it gets better is if you make a
-contribution back to the project ($$, code, or documentation).
+people, and you've gotten it for free.  No one is getting paid to answer
+your questions.  This is free software, and the only way it gets better
+is if you make a contribution back to the project ($$, code, or
+documentation).
 
 We will note that the people who get most upset about any answers to
 their questions usually do not have any intention of contributing to
@@ -145,14 +152,25 @@ someone to address your concerns.  Either way, make sure that any fix
 is contributed back to the project so that no one else runs into the
 same issue.
 
-Support is available.  See the "support" link at the top of the main
-web page:
+Books on RADIUS
+---------------
+
+See ``doc/README`` for more information about FreeRADIUS.
+
+There is an O'Reilly book available.  It serves as a good
+introduction for anyone new to RADIUS.  However, it is almost 12 years
+old, and is not much more than a basic introduction to the subject.
+
+http://www.amazon.com/exec/obidos/ASIN/0596003226/freeradiusorg-20/
+
+Commercial support
+------------------
 
-http://freeradius.org
+Technical support, managed systems support, custom deployments,
+sponsored feature development and many other commercial services
+are available from `Network RADIUS
+<http://www.networkradius.com>`_.
 
-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
diff --git a/VERSION b/VERSION
index 2451c27..a909317 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.0.7
+3.0.10
index 9e42ff2..da48acc 100644 (file)
@@ -513,17 +513,22 @@ dnl #  Check if we have the choose expr builtin
 dnl #
 AC_DEFUN([FR_HAVE_BUILTIN_CHOOSE_EXPR],
 [
-  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_choose_expr=yes],[have_builtin_choose_expr=no],[have_builtin_choose_expr=no])
-  AC_MSG_RESULT($have_builtin_choose_expr)
-  if test "x$have_builtin_choose_expr" = "xyes"; then
+  AC_CACHE_CHECK([for __builtin_choose_expr support in compiler], [ax_cv_cc_builtin_choose_expr],[
+    AC_RUN_IFELSE(
+      [
+        AC_LANG_SOURCE(
+        [
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return __builtin_choose_expr(0, 1, 0);
+          }
+        ])
+      ],
+      [ax_cv_cc_builtin_choose_expr=yes],
+      [ax_cv_cc_builtin_choose_expr=no]
+    )
+  ])
+  if test "x$ax_cv_cc_builtin_choose_expr" = "xyes"; then
     AC_DEFINE([HAVE_BUILTIN_CHOOSE_EXPR],1,[Define if the compiler supports __builtin_choose_expr])
   fi
 ])
@@ -533,17 +538,22 @@ dnl #  Check if we have the types compatible p builtin
 dnl #
 AC_DEFUN([FR_HAVE_BUILTIN_TYPES_COMPATIBLE_P],
 [
-  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_types_compatible_p=yes],[have_builtin_types_compatible_p=no],[have_builtin_types_compatible_p=no])
-  AC_MSG_RESULT($have_builtin_types_compatible_p)
-  if test "x$have_builtin_types_compatible_p" = "xyes"; then
+  AC_CACHE_CHECK([for __builtin_types_compatible_p support in compiler], [ax_cv_cc_builtin_types_compatible_p],[
+    AC_RUN_IFELSE(
+      [
+        AC_LANG_SOURCE(
+        [
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return !(__builtin_types_compatible_p(char *, char *));
+          }
+        ])
+      ],
+      [ax_cv_cc_builtin_types_compatible_p=yes],
+      [ax_cv_cc_builtin_types_compatible_p=no]
+    )
+  ])
+  if test "x$ax_cv_cc_builtin_types_compatible_p" = "xyes"; then
     AC_DEFINE([HAVE_BUILTIN_TYPES_COMPATIBLE_P],1,[Define if the compiler supports __builtin_types_compatible_p])
   fi
 ])
@@ -551,23 +561,51 @@ AC_DEFUN([FR_HAVE_BUILTIN_TYPES_COMPATIBLE_P],
 dnl #
 dnl #  Check if we have the bwsap64 builtin
 dnl #
-AC_DEFUN([FR_HAVE_BUILTIN_BSWAP_64],
+AC_DEFUN([FR_HAVE_BUILTIN_BSWAP64],
 [
+  AC_CACHE_CHECK([for __builtin_bswap64 support in compiler], [ax_cv_cc_builtin_bswap64],[
+    AC_RUN_IFELSE(
+      [
+        AC_LANG_SOURCE([
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return (__builtin_bswap64(0));
+          }
+        ])
+      ],
+      [ax_cv_cc_builtin_bswap64=yes],
+      [ax_cv_cc_builtin_bswap64=no]
+    )
+  ])
+  if test "x$ax_cv_cc_builtin_bswap64" = "xyes"; then
+    AC_DEFINE([HAVE_BUILTIN_BSWAP_64],1,[Define if the compiler supports __builtin_bswap64])
+  fi
+])
+
 dnl #
-dnl #  See if the compilation works with __thread, for thread-local storage
+dnl #  Check if we have __attribute__((__bounded__)) (usually only OpenBSD with GCC)
 dnl #
-  AC_MSG_CHECKING(for __builtin_bswap64 support in compiler)
-  AC_RUN_IFELSE(
-    [AC_LANG_SOURCE(
-      [[
-        int main(int argc, char **argv) {
-          return (__builtin_bswap64(0));
-        }
-      ]])
-    ],[have_builtin_bswap64=yes],[have_builtin_bswap64=no],[have_builtin_bswap64=no])
-  AC_MSG_RESULT($have_builtin_bswap64)
-  if test "x$have_builtin_bswap64" = "xyes"; then
-    AC_DEFINE([HAVE_BUILTIN_BSWAP_64],1,[Define if the compiler supports __builtin_types_compatible_p])
+AC_DEFUN([FR_HAVE_BOUNDED_ATTRIBUTE],[
+  AC_CACHE_CHECK([for __attribute__((__bounded__)) support in compiler], [ax_cv_cc_bounded_attribute],[
+    CFLAGS_SAVED=$CFLAGS
+    CFLAGS="$CFLAGS -Werror"
+    AC_RUN_IFELSE(
+      [
+        AC_LANG_SOURCE([
+          void test(char *buff) __attribute__ ((__bounded__ (__string__, 1, 1)));
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return 0;
+          }
+        ])
+      ],
+      [ax_cv_cc_bounded_attribute=yes],
+      [ax_cv_cc_bounded_attribute=no]
+    )
+    CFLAGS="$CFLAGS_SAVED"
+  ])
+  if test "x$ax_cv_cc_bounded_attribute" = "xyes"; then
+    AC_DEFINE(HAVE_ATTRIBUTE_BOUNDED, 1, [Define if your compiler supports the __bounded__ attribute (usually OpenBSD gcc).])
   fi
 ])
 
index 186fe80..ebcc6ba 100755 (executable)
--- a/configure
+++ b/configure
@@ -2417,9 +2417,10 @@ ac_config_headers="$ac_config_headers src/include/autoconf.h"
 
 
 
-RADIUSD_MAJOR_VERSION=`cat VERSION | sed 's/\..*//'`
-RADIUSD_MINOR_VERSION=`cat VERSION | sed 's/^[^\.]*\.//' | sed 's/\..*$//'`
-RADIUSD_INCRM_VERSION=`cat VERSION | sed 's/^.*\..*\.//' | sed 's/[\.-].*$//'`
+
+RADIUSD_MAJOR_VERSION=`cat VERSION | cut -f1 -d.`
+RADIUSD_MINOR_VERSION=`cat VERSION | cut -f2 -d.`
+RADIUSD_INCRM_VERSION=`cat VERSION | cut -f3 -d. | sed 's/[\.-].*$//'`
 
 RADIUSD_VERSION=`echo | awk -v major="$RADIUSD_MAJOR_VERSION" \
 -v minor="$RADIUSD_MINOR_VERSION" \
@@ -4786,11 +4787,11 @@ $as_echo "$ac_cv_c_bigendian" >&6; }
  case $ac_cv_c_bigendian in #(
    yes)
 
-$as_echo "#define RADIUS_BIG_ENDIAN 1" >>confdefs.h
+$as_echo "#define FR_BIG_ENDIAN 1" >>confdefs.h
 ;; #(
    no)
 
-$as_echo "#define RADIUS_LITTLE_ENDIAN 1" >>confdefs.h
+$as_echo "#define FR_LITTLE_ENDIAN 1" >>confdefs.h
 
  ;; #(
    universal)
@@ -6047,7 +6048,8 @@ fi
 done
 
 
-                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+                if test "x$WITH_THREADS" != "xno"; 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; }
 if ${ac_cv_lib_pthread_pthread_create+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -6085,12 +6087,67 @@ fi
 $as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
 if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
 
-      CFLAGS="$CFLAGS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS"
-      LIBS="-lpthread $LIBS"
+        HAVE_LPTHREAD='yes'
+        CFLAGS="$CFLAGS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS"
+        LIBS="-lpthread $LIBS"
+
 
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the compiler flag \"-pthread\"" >&5
+$as_echo_n "checking for the compiler flag \"-pthread\"... " >&6; }
+if ${ax_cv_cc_pthread_flag+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
 
-                              { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5
+
+    CFLAGS_SAVED=$CFLAGS
+    CFLAGS="$CFLAGS -Werror -pthread"
+
+    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
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ax_cv_cc_pthread_flag="yes"
+else
+  ax_cv_cc_pthread_flag="no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    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
+
+
+    CFLAGS="$CFLAGS_SAVED"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_pthread_flag" >&5
+$as_echo "$ax_cv_cc_pthread_flag" >&6; }
+
+        if test "x$ax_cv_cc_pthread_flag" != 'xyes'; then
+          CFLAGS="$CFLAGS -pthread"
+        fi
+
+
+fi
+
+
+                        if test "x$HAVE_LPTHREAD" != "xyes"; then
+      { $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
@@ -6127,19 +6184,69 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_r_pthread_create" >&5
 $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"
+
+          CFLAGS="$CFLAGS -D_THREAD_SAFE"
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the compiler flag \"-pthread\"" >&5
+$as_echo_n "checking for the compiler flag \"-pthread\"... " >&6; }
+if ${ax_cv_cc_pthread_flag+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+
+    CFLAGS_SAVED=$CFLAGS
+    CFLAGS="$CFLAGS -Werror -pthread"
+
+    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
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ax_cv_cc_pthread_flag="yes"
 else
+  ax_cv_cc_pthread_flag="no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    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
 
-          WITH_THREADS="no"
-          fail=-lpthread
 
+    CFLAGS="$CFLAGS_SAVED"
 
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_pthread_flag" >&5
+$as_echo "$ax_cv_cc_pthread_flag" >&6; }
 
+          if test "x$ax_cv_cc_pthread_flag" != 'xyes'; then
+            LIBS="-lc_r $LIBS"
+          else
+            CFLAGS="$CFLAGS -pthread"
+          fi
 
+else
+   fail=-lc_r or -lpthread
 
 fi
 
+    fi
+  fi
 
   if test "x$WITH_THREADS" != "xyes"; then
     { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building with thread support." >&5
@@ -8567,6 +8674,7 @@ $as_echo "#define HAVE_OPENSSL_SSL_H 1" >>confdefs.h
       openssl/md5.h \
       openssl/md4.h \
       openssl/sha.h \
+      openssl/ssl.h \
       openssl/ocsp.h \
       openssl/engine.h
 do :
@@ -9855,7 +9963,8 @@ for ac_func in \
   openat \
   mkdirat \
   unlinkat \
-  bindat
+  bindat \
+  dladdr
 
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
@@ -10321,7 +10430,7 @@ fi
 $as_echo "$ax_cv_cc_weverything_flag" >&6; }
 
   if test "x$ax_cv_cc_weverything_flag" = "xyes"; then
-    devflags="$devflags -Weverything -Wformat=2 -Wno-padded -Wno-gnu-zero-variadic-macro-arguments -Wno-shorten-64-to-32 -Wno-sign-conversion -Wno-conversion -Wno-switch-enum -Wno-gnu-statement-expression -Wno-extended-offsetof -Wno-cast-align -Wno-documentation-unknown-command -Wno-covered-switch-default -Wno-packed -DWITH_VERIFY_PTR=1"
+    devflags="$devflags -W -Weverything -Wformat=2 -Wno-missing-field-initializers -Wno-date-time -Wno-padded -Wno-gnu-zero-variadic-macro-arguments -Wno-shorten-64-to-32 -Wno-sign-conversion -Wno-conversion -Wno-switch-enum -Wno-gnu-statement-expression -Wno-extended-offsetof -Wno-cast-align -Wno-documentation-unknown-command -Wno-covered-switch-default -Wno-packed -Wno-reserved-id-macro -DWITH_VERIFY_PTR=1"
   else
     if test "x$GCC" = "xyes"; then
       devflags="$devflags -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -W -Wredundant-decls -Wundef -Wformat-y2k -Wno-format-extra-args -Wno-format-zero-length -Wno-cast-align -Wformat-nonliteral -Wformat-security -Wformat=2 -DWITH_VERIFY_PTR=1"
@@ -10474,30 +10583,42 @@ $as_echo "#define TLS_STORAGE_CLASS _Thread_local" >>confdefs.h
 
   { $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_choose_expr=no
+if ${ax_cv_cc_builtin_choose_expr+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    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
+See \`config.log' for more details" "$LINENO" 5; }
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-        int main(int argc, char **argv) {
-          return __builtin_choose_expr(0, 1, 0);
-        }
+
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return __builtin_choose_expr(0, 1, 0);
+          }
 
 
 _ACEOF
 if ac_fn_c_try_run "$LINENO"; then :
-  have_builtin_choose_expr=yes
+  ax_cv_cc_builtin_choose_expr=yes
 else
-  have_builtin_choose_expr=no
+  ax_cv_cc_builtin_choose_expr=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_choose_expr" >&5
-$as_echo "$have_builtin_choose_expr" >&6; }
-  if test "x$have_builtin_choose_expr" = "xyes"; then
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_builtin_choose_expr" >&5
+$as_echo "$ax_cv_cc_builtin_choose_expr" >&6; }
+  if test "x$ax_cv_cc_builtin_choose_expr" = "xyes"; then
 
 $as_echo "#define HAVE_BUILTIN_CHOOSE_EXPR 1" >>confdefs.h
 
@@ -10506,30 +10627,42 @@ $as_echo "#define HAVE_BUILTIN_CHOOSE_EXPR 1" >>confdefs.h
 
   { $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_types_compatible_p=no
+if ${ax_cv_cc_builtin_types_compatible_p+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    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
+See \`config.log' for more details" "$LINENO" 5; }
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-        int main(int argc, char **argv) {
-          return !(__builtin_types_compatible_p(char *, char *));
-        }
+
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return !(__builtin_types_compatible_p(char *, char *));
+          }
 
 
 _ACEOF
 if ac_fn_c_try_run "$LINENO"; then :
-  have_builtin_types_compatible_p=yes
+  ax_cv_cc_builtin_types_compatible_p=yes
 else
-  have_builtin_types_compatible_p=no
+  ax_cv_cc_builtin_types_compatible_p=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_types_compatible_p" >&5
-$as_echo "$have_builtin_types_compatible_p" >&6; }
-  if test "x$have_builtin_types_compatible_p" = "xyes"; then
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_builtin_types_compatible_p" >&5
+$as_echo "$ax_cv_cc_builtin_types_compatible_p" >&6; }
+  if test "x$ax_cv_cc_builtin_types_compatible_p" = "xyes"; then
 
 $as_echo "#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1" >>confdefs.h
 
@@ -10538,36 +10671,96 @@ $as_echo "#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1" >>confdefs.h
 
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_bswap64 support in compiler" >&5
 $as_echo_n "checking for __builtin_bswap64 support in compiler... " >&6; }
-  if test "$cross_compiling" = yes; then :
-  have_builtin_bswap64=no
+if ${ax_cv_cc_builtin_bswap64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    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
+See \`config.log' for more details" "$LINENO" 5; }
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-        int main(int argc, char **argv) {
-          return (__builtin_bswap64(0));
-        }
+
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return (__builtin_bswap64(0));
+          }
 
 
 _ACEOF
 if ac_fn_c_try_run "$LINENO"; then :
-  have_builtin_bswap64=yes
+  ax_cv_cc_builtin_bswap64=yes
 else
-  have_builtin_bswap64=no
+  ax_cv_cc_builtin_bswap64=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_bswap64" >&5
-$as_echo "$have_builtin_bswap64" >&6; }
-  if test "x$have_builtin_bswap64" = "xyes"; then
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_builtin_bswap64" >&5
+$as_echo "$ax_cv_cc_builtin_bswap64" >&6; }
+  if test "x$ax_cv_cc_builtin_bswap64" = "xyes"; then
 
 $as_echo "#define HAVE_BUILTIN_BSWAP_64 1" >>confdefs.h
 
   fi
 
 
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((__bounded__)) support in compiler" >&5
+$as_echo_n "checking for __attribute__((__bounded__)) support in compiler... " >&6; }
+if ${ax_cv_cc_bounded_attribute+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    CFLAGS_SAVED=$CFLAGS
+    CFLAGS="$CFLAGS -Werror"
+    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
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+          void test(char *buff) __attribute__ ((__bounded__ (__string__, 1, 1)));
+          int main(int argc, char **argv) {
+            if ((argc < 0) || !argv) return 1; /* -Werror=unused-parameter */
+            return 0;
+          }
+
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ax_cv_cc_bounded_attribute=yes
+else
+  ax_cv_cc_bounded_attribute=no
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    CFLAGS="$CFLAGS_SAVED"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cc_bounded_attribute" >&5
+$as_echo "$ax_cv_cc_bounded_attribute" >&6; }
+  if test "x$ax_cv_cc_bounded_attribute" = "xyes"; then
+
+$as_echo "#define HAVE_ATTRIBUTE_BOUNDED 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; }
index 4c294d0..d1921f1 100644 (file)
@@ -33,10 +33,9 @@ dnl #
 dnl #  Custom hackery to discover version at configure time
 dnl #
 dnl #############################################################
-
-RADIUSD_MAJOR_VERSION=`cat VERSION | sed 's/\..*//'`
-RADIUSD_MINOR_VERSION=`cat VERSION | sed 's/^[[^\.]]*\.//' | sed 's/\..*$//'`
-RADIUSD_INCRM_VERSION=`cat VERSION | sed 's/^.*\..*\.//' | sed 's/[[\.-]].*$//'`
+RADIUSD_MAJOR_VERSION=`cat VERSION | cut -f1 -d.`
+RADIUSD_MINOR_VERSION=`cat VERSION | cut -f2 -d.`
+RADIUSD_INCRM_VERSION=`cat VERSION | cut -f3 -d. | sed 's/[[\.-]].*$//'`
 
 RADIUSD_VERSION=`echo | awk -v major="$RADIUSD_MAJOR_VERSION" \
 -v minor="$RADIUSD_MINOR_VERSION" \
@@ -167,8 +166,8 @@ dnl #  check for system bytesex
 dnl #  AC_DEFINES WORDS_BIGENDIAN
 dnl #
 AC_C_BIGENDIAN(
-  [AC_DEFINE(RADIUS_BIG_ENDIAN, 1, [Define if your processor stores words with the most significant byte first])],
-  [AC_DEFINE(RADIUS_LITTLE_ENDIAN, 1, [Define if your processor stores words with the least significant byte first])]
+  [AC_DEFINE(FR_BIG_ENDIAN, 1, [Define if your processor stores words with the most significant byte first])],
+  [AC_DEFINE(FR_LITTLE_ENDIAN, 1, [Define if your processor stores words with the least significant byte first])]
 )
 
 dnl #
@@ -711,25 +710,51 @@ if test "x$WITH_THREADS" = "xyes"; then
   dnl #  On Some systems, we need extra pre-processor flags, to get them to
   dnl #  to do the threading properly.
   dnl #
-  AC_CHECK_LIB(pthread, pthread_create,
-    [
-      CFLAGS="$CFLAGS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS"
-      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 #
+  if test "x$WITH_THREADS" != "xno"; then
+    AC_CHECK_LIB(pthread, pthread_create,
+      [
+        HAVE_LPTHREAD='yes'
+        CFLAGS="$CFLAGS -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS"
+        LIBS="-lpthread $LIBS"
+
+        dnl #
+        dnl #  -pthread should add all required CPP definitions and linker
+        dnl #  arguments. But not all compilers support it, or some compilers
+        dnl #  only support it on certain platforms.
+        dnl #
+        AX_CC_PTHREAD_FLAG
+        if test "x$ax_cv_cc_pthread_flag" != 'xyes'; then
+          CFLAGS="$CFLAGS -pthread"
+        fi
+      ]
+    )
+
+    dnl #
+    dnl #  Check for libc_r which used to be the threading library used
+    dnl #  for FreeBSD. Internet says it may be deprecated, but if we
+    dnl #  can't find lpthread it's probably worth checking.
+    dnl #
+    if test "x$HAVE_LPTHREAD" != "xyes"; then
       AC_CHECK_LIB(c_r, pthread_create,
-        [ CFLAGS="$CFLAGS -pthread -D_THREAD_SAFE" ],
         [
-          WITH_THREADS="no"
-          fail=[-lpthread]
-        ]
+          CFLAGS="$CFLAGS -D_THREAD_SAFE"
+
+          dnl #
+          dnl #  -pthread should add all required CPP definitions and linker
+          dnl #  arguments. But not all compilers support it, or some compilers
+          dnl #  only support it on certain platforms.
+          dnl #
+          AX_CC_PTHREAD_FLAG
+          if test "x$ax_cv_cc_pthread_flag" != 'xyes'; then
+            LIBS="-lc_r $LIBS"
+          else
+            CFLAGS="$CFLAGS -pthread"
+          fi
+        ],
+        [ fail=[-lc_r or -lpthread] ]
       )
-    ]
-  )
+    fi
+  fi
 
   if test "x$WITH_THREADS" != "xyes"; then
     AC_MSG_WARN([silently not building with thread support.])
@@ -764,7 +789,7 @@ else
 fi
 
 dnl #
-dnl #  Check if we need -lsocket
+dnl #  Check if we have -ldl
 dnl #
 AC_CHECK_LIB(dl, dlopen)
 
@@ -1088,6 +1113,7 @@ if test "x$WITH_OPENSSL" = xyes; then
       openssl/md5.h \
       openssl/md4.h \
       openssl/sha.h \
+      openssl/ssl.h \
       openssl/ocsp.h \
       openssl/engine.h,
       [ OPENSSL_CPPFLAGS="$smart_include" ],
@@ -1440,7 +1466,8 @@ AC_CHECK_FUNCS( \
   openat \
   mkdirat \
   unlinkat \
-  bindat
+  bindat \
+  dladdr
 )
 
 AC_TYPE_SIGNAL
@@ -1557,7 +1584,7 @@ if test "x$developer" = "xyes"; then
   dnl #
   AX_CC_WEVERYTHING_FLAG
   if test "x$ax_cv_cc_weverything_flag" = "xyes"; then
-    devflags="$devflags -Weverything -Wformat=2 -Wno-padded -Wno-gnu-zero-variadic-macro-arguments -Wno-shorten-64-to-32 -Wno-sign-conversion -Wno-conversion -Wno-switch-enum -Wno-gnu-statement-expression -Wno-extended-offsetof -Wno-cast-align -Wno-documentation-unknown-command -Wno-covered-switch-default -Wno-packed -DWITH_VERIFY_PTR=1"
+    devflags="$devflags -W -Weverything -Wformat=2 -Wno-missing-field-initializers -Wno-date-time -Wno-padded -Wno-gnu-zero-variadic-macro-arguments -Wno-shorten-64-to-32 -Wno-sign-conversion -Wno-conversion -Wno-switch-enum -Wno-gnu-statement-expression -Wno-extended-offsetof -Wno-cast-align -Wno-documentation-unknown-command -Wno-covered-switch-default -Wno-packed -Wno-reserved-id-macro -DWITH_VERIFY_PTR=1"
   else
     if test "x$GCC" = "xyes"; then
       devflags="$devflags -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -W -Wredundant-decls -Wundef -Wformat-y2k -Wno-format-extra-args -Wno-format-zero-length -Wno-cast-align -Wformat-nonliteral -Wformat-security -Wformat=2 -DWITH_VERIFY_PTR=1"
@@ -1608,7 +1635,8 @@ dnl #
 FR_TLS
 FR_HAVE_BUILTIN_CHOOSE_EXPR
 FR_HAVE_BUILTIN_TYPES_COMPATIBLE_P
-FR_HAVE_BUILTIN_BSWAP_64
+FR_HAVE_BUILTIN_BSWAP64
+FR_HAVE_BOUNDED_ATTRIBUTE
 
 dnl #############################################################
 dnl #
index 4ee4442..547305a 100644 (file)
@@ -8,6 +8,10 @@ freeradius-mysql
 freeradius-postgresql
 freeradius-rest
 freeradius-utils
+freeradius-dhcp
+freeradius-memcached
+freeradius-redis
+freeradius-yubikey
 freeradius
 libfreeradius-dev
 libfreeradius3
index a5ae803..19b0c79 100644 (file)
@@ -1,3 +1,21 @@
+freeradius (3.0.10+git) unstable; urgency=medium
+
+  * New upstream version.
+
+ -- Alan DeKok <aland@freeradius.org>  Wed, 08 Jul 2015 14:00:00 -0400
+
+freeradius (3.0.9+git) unstable; urgency=medium
+
+  * New upstream version.
+
+ -- Alan DeKok <aland@freeradius.org>  Wed, 22 Apr 2015 13:30:00 -0400
+
+freeradius (3.0.8+git) unstable; urgency=medium
+
+  * New upstream version.
+
+ -- Alan DeKok <aland@freeradius.org>  Thu, 19 Feb 2015 12:00:00 -0400
+
 freeradius (3.0.7+git) unstable; urgency=medium
 
   * New upstream version.
index 7f8f011..ec63514 100644 (file)
@@ -1 +1 @@
-7
+9
index 129a508..34e8e5e 100644 (file)
@@ -1,5 +1,5 @@
 Source: freeradius
-Build-Depends: debhelper (>= 7.4),
+Build-Depends: debhelper (>= 9),
  quilt,
  dpkg-dev (>= 1.13.19),
  autotools-dev,
@@ -7,8 +7,8 @@ Build-Depends: debhelper (>= 7.4),
  libcap-dev,
  libgdbm-dev,
  libiodbc2-dev,
- libjson-c2 | libjson0,
- libjson-c-dev | libjson0-dev,
+ libjson0 | libjson-c2,
+ libjson0-dev | libjson-c-dev,
  libkrb5-dev,
  libldap2-dev,
  libpam0g-dev,
@@ -21,13 +21,17 @@ Build-Depends: debhelper (>= 7.4),
  libsqlite3-dev,
  libssl-dev,
  libtalloc-dev,
+ libwbclient-dev,
  libyubikey-dev,
+ libykclient-dev,
+ libmemcached-dev,
+ libhiredis-dev,
  python-dev
 Section: net
 Priority: optional
 Maintainer: Josip Rodin <joy-packages@debian.org>
 Uploaders: Stephen Gran <sgran@debian.org>, Mark Hymers <mhy@debian.org>
-Standards-Version: 3.8.3
+Standards-Version: 3.9.6
 Homepage: http://www.freeradius.org/
 
 Package: freeradius
@@ -35,13 +39,14 @@ Architecture: any
 Depends: lsb-base (>= 3.1-23.2), ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}, freeradius-common, freeradius-config, libfreeradius3 (= ${binary:Version}), ssl-cert, adduser
 Provides: radius-server
 Recommends: freeradius-utils
-Suggests: freeradius-ldap, freeradius-postgresql, freeradius-mysql, freeradius-krb5
-Description: high-performance and highly configurable RADIUS server
+Suggests: freeradius-ldap, freeradius-postgresql, freeradius-mysql, freeradius-krb5, snmp
+Description: high-performance and highly configurable RADIUS server
  FreeRADIUS is a high-performance RADIUS server with support for:
-  - Many vendor-specific attributes.
+  - Authentication by local files, SQL, Kerberos, LDAP, PAM, and more.
+  - Powerful policy configuration language.
   - Proxying and replicating requests by any criteria.
-  - Authentication on system passwd, SQL, Kerberos, LDAP, users file, or PAM.
-  - Multiple DEFAULT configurations.
+  - Support for many EAP types; TLS, PEAP, TTLS, etc.
+  - Many vendor-specific attributes.
   - Regexp matching in string attributes.
  and lots more.
 
@@ -56,11 +61,11 @@ Description: FreeRADIUS common files
 
 Package: freeradius-config
 Architecture: any
-Depends: freeradius-common (>= 3), ${misc:Depends}
+Depends: freeradius-common (>= 3), ${misc:Depends}, openssl
 Breaks: freeradius-config
 Description: FreeRADIUS default config files
 This package should be used as a base for a site local packages
 to configure the FreeRADIUS server.
+ This package should be used as a base for a site local packages
+ to configure the FreeRADIUS server.
 
 Package: freeradius-utils
 Architecture: any
@@ -99,6 +104,13 @@ Description: FreeRADIUS shared library development files
  .
  This package contains the development headers and static library version.
 
+Package: freeradius-dhcp
+Architecture: any
+Depends: freeradius (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}
+Description: DHCP module for FreeRADIUS server
+ The FreeRADIUS server can act as a DHCP server, and this module
+ is necessary for that.
+
 Package: freeradius-krb5
 Architecture: any
 Depends: freeradius (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}
@@ -141,6 +153,27 @@ Description: iODBC module for FreeRADIUS server
  The FreeRADIUS server can use iODBC to access databases to authenticate users
  and do accounting, and this module is necessary for that.
 
+Package: freeradius-redis
+Architecture: any
+Depends: freeradius (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}
+Description: Redis module for FreeRADIUS server
+ This module is required to enable the FreeRADIUS server to access
+ Redis databases.
+
+Package: freeradius-memcached
+Architecture: any
+Depends: freeradius (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}
+Description: Memcached module for FreeRADIUS server
+ The FreeRADIUS server can cache data in memcached and this package
+ contains the required module.
+
+Package: freeradius-yubikey
+Architecture: any
+Depends: freeradius (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}
+Description: Yubikey module for FreeRADIUS server
+ This package is required to add Yubikey functionality to the
+ FreeRADIUS server.
+
 Package: freeradius-dbg
 Architecture: any
 Section: debug
index 2dd87c6..e9392fa 100644 (file)
@@ -1,61 +1,99 @@
-This package was debianized by Chad Miller <cmiller@debian.org> on
-Fri, 24 Nov 2000 16:25:57 -0500.
-The packaging was rearranged by Paul Hampson <Paul.Hampson@anu.edu.au> on
-Sun,  4 May 2003 03:51:20 +1000
-The packaging was revamped by Stephen Gran <sgran@debian.org> on
-Sat, 15 Mar 2008 16:26:51 +0000.
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: FreeRADIUS server
+Source: https://github.com/FreeRADIUS/freeradius-server
+Files-Excluded: doc/rfc/*
+Comment: This is a DEP-5 machine readable version of the debian/copyright file
+ shipped with earlier versions of FreeRADIUS. It is not yet comprehensive. See
+ individual files for their copyright and license.
+ http://dep.debian.net/deps/dep5/
+
+Files: *
+Copyright: 2000-2014, The FreeRADIUS Server Project
+ 1997-1999, Cistron Internet Services B.V.
+License: GPL-2+
+
+Files:debian/*
+Copyright: 2008, Stephen Gran <sgran@debian.org>
+License: GPL-2+
+Comment: This package was debianized by Chad Miller <cmiller@debian.org> on
+ Fri, 24 Nov 2000 16:25:57 -0500.
+ The packaging was rearranged by Paul Hampson <Paul.Hampson@anu.edu.au> on
+ Sun,  4 May 2003 03:51:20 +1000
+ The packaging was revamped by Stephen Gran <sgran@debian.org> on
+ Sat, 15 Mar 2008 16:26:51 +0000.
+ It was downloaded from http://www.freeradius.org/
+
+Files: src/*
+Copyright: 2000-2014, The FreeRADIUS Server Project
+ 1997-1999, Cistron Internet Services B.V.
+License: GPL-2+ with OpenSSL exception
+
+Files: src/lib/*
+Copyright: See individual files
+License: LGPL-2.1+
+ 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 of the License, or (at your option) any later version.
+ .
+ On Debian systems, the complete text of the GNU Lesser General Public
+ License can be found in /usr/share/common-licenses/LGPL-2.1.
+
+License: GPL-2+
+ 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 package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA  02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2'.
+
+License: GPL-2+ with OpenSSL exception
+ 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.
+ .
+ In addition, as a special exception, the author of this
+ program gives permission to link the code of its
+ release with the OpenSSL project's "OpenSSL" library (or
+ with modified versions of it that use the same license as
+ the "OpenSSL" library), and distribute the linked
+ executables. You must obey the GNU General Public
+ License in all respects for all of the code used other
+ than "OpenSSL".  If you modify this file, you may extend
+ this exception to your version of the file, but you are
+ not obligated to do so.  If you do not wish to do so,
+ delete this exception statement from your 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 package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA  02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2'.
 
-It was downloaded from http://www.freeradius.org/
 
-Copyright: 
 
-Copyright (C) 2000-2014 The FreeRADIUS Server Project
-Copyright (C) 1997-1999 Cistron Internet Services B.V.
-
-License:
-
-Except for /usr/lib/freeradius/libradius*, this package is licensed
-under the GNU GPL version 2.
-
-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.
-
-On Debian systems, the complete text of the GNU General Public License can be
-found in /usr/share/common-licenses/GPL-2 file.
-
---
-
-/usr/lib/freeradius/libradius* is under the GNU LGPL version 2.
-
-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 of the License, or (at your option) any later version.
-
-On Debian systems, the complete text of the GNU Lesser General Public
-License can be found in /usr/share/common-licenses/LGPL.
-
---
-
-src/LICENSE.openssl includes a modification to the main LICENSE file,
-which is GPLv2. It applies only to the files in the "src" directory.
-(That directory contains the source code which has links to OpenSSL
-and from which the Debian binaries are produced.)
-
-In addition, as a special exception, the copyright holders give
-permission to link the code of this program with the OpenSSL library,
-and distribute linked combinations including the two.
-You must obey the GNU General Public License in all respects
-for all of the code used other than OpenSSL.  If you modify
-file(s) with this exception, you may extend this exception to your
-version of the file(s), but you are not obligated to do so.  If you
-do not wish to do so, delete this exception statement from your
-version.  If you delete this exception statement from all source
-files in the program, then also delete it here.
-
---
-
-The Debian packaging is (C) 2008, Stephen Gran <sgran@debian.org> and
-is licensed under the GPL, see /usr/share/common-licenses/GPL.
diff --git a/debian/freeradius-common.lintian-overrides b/debian/freeradius-common.lintian-overrides
new file mode 100644 (file)
index 0000000..7c2fc40
--- /dev/null
@@ -0,0 +1,7 @@
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-common: extended-description-is-probably-too-short
+
+# There are a lot of these that should probably be tidied up, but
+# it's not a great priority.
+freeradius-common: hyphen-used-as-minus-sign
index e37bcbd..4961410 100644 (file)
@@ -1,3 +1,10 @@
 freeradius-config: breaks-without-version
 freeradius-config: package-relation-with-self
+
+# There are example python scripts in the config, but it's the freeradius
+# package that includes dependencies on the python libraries.
 freeradius-config: python-script-but-no-python-dep etc/freeradius/mods-config/python/*.py
+
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-config: extended-description-is-probably-too-short
diff --git a/debian/freeradius-dhcp.install b/debian/freeradius-dhcp.install
new file mode 100644 (file)
index 0000000..aefb0a0
--- /dev/null
@@ -0,0 +1,3 @@
+usr/lib/freeradius/rlm_dhcp*.so
+usr/lib/freeradius/proto_dhcp*.so
+usr/lib/freeradius/libfreeradius-dhcp.so
diff --git a/debian/freeradius-dhcp.lintian-overrides b/debian/freeradius-dhcp.lintian-overrides
new file mode 100644 (file)
index 0000000..dd30c85
--- /dev/null
@@ -0,0 +1,3 @@
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-dhcp: extended-description-is-probably-too-short
diff --git a/debian/freeradius-dhcp.postinst b/debian/freeradius-dhcp.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 7e24109..a157baa 100644 (file)
@@ -1 +1,5 @@
 freeradius-iodbc: binary-or-shlib-defines-rpath
+
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-iodbc: extended-description-is-probably-too-short
index ae4e075..d3cced5 100644 (file)
@@ -1 +1,3 @@
-freeradius-krb5: binary-or-shlib-defines-rpath
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-krb5: extended-description-is-probably-too-short
index a18fc0c..ee27d07 100644 (file)
@@ -1 +1,3 @@
-freeradius-ldap: binary-or-shlib-defines-rpath
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-ldap: extended-description-is-probably-too-short
diff --git a/debian/freeradius-memcached.install b/debian/freeradius-memcached.install
new file mode 100644 (file)
index 0000000..738a641
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/freeradius/rlm_cache_memcached.so
diff --git a/debian/freeradius-memcached.lintian-overrides b/debian/freeradius-memcached.lintian-overrides
new file mode 100644 (file)
index 0000000..15117f9
--- /dev/null
@@ -0,0 +1,3 @@
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-memcached: extended-description-is-probably-too-short
diff --git a/debian/freeradius-memcached.postinst b/debian/freeradius-memcached.postinst
new file mode 100755 (executable)
index 0000000..1b074d9
--- /dev/null
@@ -0,0 +1,19 @@
+#! /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
+       ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/freeradius-mysql.lintian-overrides b/debian/freeradius-mysql.lintian-overrides
new file mode 100644 (file)
index 0000000..874389c
--- /dev/null
@@ -0,0 +1,3 @@
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-mysql: extended-description-is-probably-too-short
index 5f887bc..9aee5e8 100644 (file)
@@ -1 +1,5 @@
 freeradius-postgresql: binary-or-shlib-defines-rpath
+
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-postgresql: extended-description-is-probably-too-short
diff --git a/debian/freeradius-redis.install b/debian/freeradius-redis.install
new file mode 100644 (file)
index 0000000..87c4ac5
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/freeradius/rlm_redis*.so
diff --git a/debian/freeradius-redis.lintian-overrides b/debian/freeradius-redis.lintian-overrides
new file mode 100644 (file)
index 0000000..c4efd20
--- /dev/null
@@ -0,0 +1,3 @@
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-redis: extended-description-is-probably-too-short
diff --git a/debian/freeradius-redis.postinst b/debian/freeradius-redis.postinst
new file mode 100755 (executable)
index 0000000..1b074d9
--- /dev/null
@@ -0,0 +1,19 @@
+#! /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
+       ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/debian/freeradius-rest.lintian-overrides b/debian/freeradius-rest.lintian-overrides
new file mode 100644 (file)
index 0000000..8e23680
--- /dev/null
@@ -0,0 +1,3 @@
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-rest: extended-description-is-probably-too-short
index 1278a43..f1a4d58 100644 (file)
@@ -1,6 +1,7 @@
 usr/bin/rlm_ippool_tool
 usr/bin/smbencrypt
 usr/bin/radclient
+usr/bin/radeapclient
 usr/bin/radwho
 usr/bin/radsniff
 usr/bin/radlast
diff --git a/debian/freeradius-utils.lintian-overrides b/debian/freeradius-utils.lintian-overrides
deleted file mode 100644 (file)
index b9e3b4f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-freeradius-utils: binary-or-shlib-defines-rpath
diff --git a/debian/freeradius-yubikey.install b/debian/freeradius-yubikey.install
new file mode 100644 (file)
index 0000000..3119a4c
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/freeradius/rlm_yubikey.so
diff --git a/debian/freeradius-yubikey.lintian-overrides b/debian/freeradius-yubikey.lintian-overrides
new file mode 100644 (file)
index 0000000..2977cdc
--- /dev/null
@@ -0,0 +1,3 @@
+# There's plenty in the description of this package to identify
+# what it does.
+freeradius-yubikey: extended-description-is-probably-too-short
diff --git a/debian/freeradius-yubikey.postinst b/debian/freeradius-yubikey.postinst
new file mode 100755 (executable)
index 0000000..1b074d9
--- /dev/null
@@ -0,0 +1,19 @@
+#! /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
+       ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
+
index 91ec2f1..a281e62 100755 (executable)
@@ -112,4 +112,4 @@ case "$1" in
         ;;
 esac
 
-exit 0
+exit $ret
index eef7072..6ec1ada 100644 (file)
@@ -1,4 +1,5 @@
 usr/lib/freeradius/rlm*.so
+usr/lib/freeradius/proto_vmps.so
 usr/sbin/checkrad
 usr/sbin/freeradius
 usr/sbin/raddebug
index bd934f6..6d276ab 100644 (file)
@@ -1 +1,7 @@
 freeradius: binary-or-shlib-defines-rpath
+
+# There's just too much documentation to register, and most of it isn't a
+# "book" or similar that really makes sense anyway. The lintian check is just
+# reporting a single file that happens to be .html in a sea of .txt and .rst
+# files...
+freeradius: possible-documentation-but-no-doc-base-registration
index 835bea5..9ecb074 100644 (file)
@@ -1 +1,3 @@
-libfreeradius3: binary-or-shlib-defines-rpath
+# There's plenty in the description of this package to identify
+# what it does.
+libfreeradius3: extended-description-is-probably-too-short
diff --git a/debian/patches/disable-dhcp-bydefault.diff b/debian/patches/disable-dhcp-bydefault.diff
new file mode 100644 (file)
index 0000000..a76a085
--- /dev/null
@@ -0,0 +1,12 @@
+diff a/raddb/all.mk b/raddb/all.mk
+--- a/raddb/all.mk
++++ b/raddb/all.mk
+@@ -8,7 +8,7 @@ DEFAULT_SITES :=       default inner-tunnel
+ LOCAL_SITES :=                $(addprefix raddb/sites-enabled/,$(DEFAULT_SITES))
+ DEFAULT_MODULES :=    always attr_filter cache_eap chap \
+-                      detail detail.log digest dhcp dynamic_clients eap \
++                      detail detail.log digest dynamic_clients eap \
+                       echo exec expiration expr files linelog logintime \
+                       mschap ntlm_auth pap passwd preprocess radutmp realm \
+                       replicate soh sradutmp unix unpack utf8
diff --git a/debian/patches/logrotate-path.diff b/debian/patches/logrotate-path.diff
new file mode 100644 (file)
index 0000000..6d1871f
--- /dev/null
@@ -0,0 +1,54 @@
+diff --git a/scripts/logrotate/freeradius b/scripts/logrotate/freeradius
+index cbeeb5f..bfb8220 100644
+--- a/scripts/logrotate/freeradius
++++ b/scripts/logrotate/freeradius
+@@ -1,7 +1,7 @@
+ #
+ #  Sample logrotate file for FreeRADIUS
+ #
+-#  You can use this to rotate the /var/log/radius/* files, simply copy it to /etc/logrotate.d/radiusd
++#  You can use this to rotate the /var/log/freeradius/* files, simply copy it to /etc/logrotate.d/radiusd
+ #
+ #
+@@ -17,28 +17,28 @@ notifempty
+ #
+ #  The main server log
+ #
+-/var/log/radius/radius.log {
++/var/log/freeradius/radius.log {
+       copytruncate
+ }
+ #
+ #  Session monitoring utilities
+ #
+-/var/log/radius/checkrad.log /var/log/radius/radwatch.log {
++/var/log/freeradius/checkrad.log /var/log/freeradius/radwatch.log {
+       nocreate
+ }
+ #
+ #  Session database modules
+ #
+-/var/log/radius/radutmp /var/log/radius/radwtmp {
++/var/log/freeradius/radutmp /var/log/freeradius/radwtmp {
+       nocreate
+ }
+ #
+ #  SQL log files
+ #
+-/var/log/radius/sqllog.sql {
++/var/log/freeradius/sqllog.sql {
+       nocreate
+ }
+@@ -49,6 +49,6 @@ notifempty
+ # (or similar) in radiusd.conf, without rotation.  If you go with the
+ # second technique, you will need another cron job that removes old
+ # detail files.  You do not need to comment out the below for method #2.
+-/var/log/radius/radacct/*/detail {
++/var/log/freeradius/radacct/*/detail {
+       nocreate
+ }
index 9f2754b..a0c089d 100644 (file)
@@ -1,6 +1,8 @@
---- a/Make.inc.in
-+++ b/Make.inc.in
-@@ -94,7 +94,7 @@
+Index: freeradius-server/Make.inc.in
+===================================================================
+--- freeradius-server.orig/Make.inc.in
++++ freeradius-server/Make.inc.in
+@@ -95,7 +95,7 @@ LDFLAGS              = $(OPENSSL_LDFLAGS) $(TALLOC_L
  
  LOGDIR                = ${logdir}
  RADDBDIR      = ${raddbdir}
@@ -9,9 +11,11 @@
  SBINDIR               = ${sbindir}
  RADIR         = ${radacctdir}
  LIBRADIUS     = $(top_builddir)/src/lib/$(LIBPREFIX)freeradius-radius.la $(TALLOC_LIBS)
---- a/raddb/radiusd.conf.in
-+++ b/raddb/radiusd.conf.in
-@@ -61,7 +61,7 @@
+Index: freeradius-server/raddb/radiusd.conf.in
+===================================================================
+--- freeradius-server.orig/raddb/radiusd.conf.in
++++ freeradius-server/raddb/radiusd.conf.in
+@@ -61,7 +61,7 @@ radacctdir = @radacctdir@
  
  #
  #  name of the running server.  See also the "-n" command-line option.
@@ -20,7 +24,7 @@
  
  #  Location of config and logfiles.
  confdir = ${raddbdir}
-@@ -415,8 +415,8 @@
+@@ -436,8 +436,8 @@ security {
        #  member.  This can allow for some finer-grained access
        #  controls.
        #
  
        #  Core dumps are a bad thing.  This should only be set to
        #  'yes' if you're debugging a problem with the server.
---- a/scripts/monit/freeradius.monitrc
-+++ b/scripts/monit/freeradius.monitrc
+Index: freeradius-server/scripts/monit/freeradius.monitrc
+===================================================================
+--- freeradius-server.orig/scripts/monit/freeradius.monitrc
++++ freeradius-server/scripts/monit/freeradius.monitrc
 @@ -8,9 +8,9 @@
  #  Totalmem limit should be lowered to 200.0 if none of the 
  #  interpreted language modules or rlm_cache are being used.
index 5ba2d18..886c023 100644 (file)
@@ -1 +1,3 @@
 radiusd-to-freeradius.diff
+disable-dhcp-bydefault.diff
+logrotate-path.diff
index ceb2800..dee6873 100755 (executable)
@@ -12,6 +12,9 @@
 
 # Uncomment this to turn on verbose mode.
 export DH_VERBOSE=1
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/default.mk
+
 
 .NOTPARALLEL:
 
@@ -26,7 +29,7 @@ logdir          = /var/log/$(package)
 pkgdocdir       = /usr/share/doc/$(package)
 raddbdir        = /etc/$(package)
 
-modulelist=krb5 ldap sql_mysql sql_iodbc sql_postgresql
+modulelist=krb5 ldap sql_mysql sql_iodbc sql_postgresql dhcp redis rest yubikey
 pkgs=$(shell dh_listpackages)
 
 # This has to be exported to make some magic below work.
@@ -93,6 +96,7 @@ endif
                --without-rlm_eap_tnc \
                --with-rlm_sql_postgresql_lib_dir=`pg_config --libdir` \
                --with-rlm_sql_postgresql_include_dir=`pg_config --includedir` \
+               --with-iodbc-include-dir='/usr/include/iodbc' \
                --without-rlm_eap_ikev2 \
                --without-rlm_sql_oracle \
                --without-rlm_sql_unixodbc
@@ -105,7 +109,7 @@ endif
 #Architecture
 build: patch build-arch build-indep
 
-build-arch: build-arch-stamp
+build-arch: build-arch-stamp patch
 build-arch-stamp: config.status
        $(MAKE)
        touch $@
@@ -163,6 +167,9 @@ install-arch: build-arch-stamp
          rm -f $(freeradius_dir)/usr/lib/freeradius/rlm_$$mod*.so ; \
        done
 
+       dh_install --sourcedir=$(freeradius_dir) -p freeradius-memcached
+       rm -f $(freeradius_dir)/usr/lib/freeradius/rlm_cache_memcached.so
+
        dh_install --sourcedir=$(freeradius_dir) -p freeradius-utils
        dh_install --sourcedir=$(freeradius_dir) -p freeradius
 
diff --git a/debian/upstream/signing-key.asc b/debian/upstream/signing-key.asc
new file mode 100644 (file)
index 0000000..d25b342
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
+mQCNAzx7wFMAAAEEALq2yahNGENq7Z8xqIaaxlMYPEqdnWme+QQRobX+0mHJ+xjv
+uU9icVaQJrgrcgmH9Sx5avAZViypk/bBSwxUxbUZfF9LRsEPJB2Rpg2eLuxShYiE
+x0CMCAIQvDFCmygm4+dqgkj1/BCImki8nvQIoW56uTTkskZuq6kul4vkAkl9AAUR
+tCRBbGFuIFQuIERlS29rIDxhbGFuZEBmcmVlcmFkaXVzLm9yZz6JAJUDBRA8e8BT
+qS6Xi+QCSX0BAXvOA/wPxVKQXtyfQSFi8WrPa0QUaRzm8j9Kna9u9Xn2wzF18neH
+ogxzDIdJZtB2zDRKaRbNeYrcz0LnC5sxZqMco0NkI7P2ifE42aWXauSuYaYA9uG6
+kP+CFjprorK0Cc6NUL47nWxB5x5zkix85MUjkMbOFyrZrUKKcHAeWfjzMf0Vkg==
+=VwDM
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/debian/watch b/debian/watch
new file mode 100644 (file)
index 0000000..5bb4676
--- /dev/null
@@ -0,0 +1,9 @@
+version=3
+
+opts=pasv,\
+pgpsigurlmangle=s/$/.sig/,\
+repacksuffix=+dfsg,\
+dversionmangle=s/\+git$//,\
+uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/\
+  ftp://ftp.freeradius.org/pub/freeradius/freeradius-server-(.+)\.tar\.gz
+
index b335111..908e269 100644 (file)
@@ -1,3 +1,291 @@
+FreeRADIUS 3.0.10 Mon 05 Oct 2015 15:00:00 EDT urgency=medium
+       Feature improvements
+       * Do more optimization of unlang policies.  This makes
+         run-time a bit faster.
+       * Re-name most of the functions in src/lib.  Third-party
+         module authors will have to do the same.
+       * More documentation on contributing and how to write
+         modules.
+       * Update radiusd.service for systemd.
+       * Open IPv6 proxy socket if the server is listening on IPV6
+         auth / acct / coa packets.
+       * Create debian packages for DHCP.  Fixes #1125.
+       * Add more tests for "update" section parsing.
+       * Update "man" pages.
+       * Update attributes for Alcatel 7750
+       * Add dictionary for Boingo Wi-Fi
+       * Add support for DHCP lease queries.
+         See raddb/sites-available/dhcp
+       * On HUP, check all modules for config files which have
+         changed.  And only re-load those modules.
+       * Allow FreeRADIUS-Response-Delay(-USec) to be set for
+         RADIUS packets.  Patch from Herwin Weststrate.
+       * Documentation fixes from Alan Buxey and Matthew Newton.
+       * Update "logrotate" script.
+       * Added more RFCs to doc/rfc for new standards implemented
+         by FreeRADIUS.
+       * Don't crash when doing "radmin -e "help hup".
+         Patch from Matthew Newton.
+       * The dictionary parser now does more sanity checks, which
+         prevents run-time problems with invalid attributes.
+       * Update debian packages.  Patches from Christopher Hoskin.
+       * Many other debian packaging fixes from Matthew Netwon
+         and Herwin Weststrate.
+       * Add "session-state" to Perl.  Patch from Herwin Weststrate.
+
+       Bug fixes
+       * Fix rlm_files so that there are no collisions when loading
+         10's of 1000's of users.
+       * Fix radclient to use our internal v4/v6 parsing functions.
+         v6 addresses with ports now work correctly.
+       * Fix sending/receiving packet messages to wrap v6 addresses
+         in square brackets '[]'.
+       * Check for sasl/sasl.h when building rlm_ldap, and disable
+         SASL functionality if unavailable.
+       * Fix issue which caused a non \0 terminated buffer to be
+         assigned to attributes if the value being assigned contained
+         an invalid escape sequence.
+       * Fix deadlock when reconnecting connections in the connection
+         pool.
+       * Fix potential overrun in functions that used fr_utf8_char
+         with a non nul terminated buffer.
+       * Fix decoding issue for Tunnel-Password type attributes
+         which were very long.  Found by Denis Andzakovic.
+       * Fix radclient issue with TCP sockets on FreeBSD.
+       * The server now creates ${run_dir} and ${logdir} directories
+         in daemon mode, when running as "root".
+       * Handle tags when using maps.  Fixes #1191.
+       * Fix crash when CoA packets time out.
+       * Fix parse error in rediswho
+       * Fix regex support in SQL radcheck the "users" file and radsniff.
+       * Register listen xlat earlier, so that it's available when the
+         virtual servers are being parsed.
+       * Parse Ascend-Data-Filter when given as "0x..."
+       * Print Ascend-Data-Filter correctly.  Add test cases for both.
+       * Allow old-style clients again.  They will be disallowed for
+         3.1.0 and following.
+       * Complain instead of crash when "else" and "elsif" are in
+         the wrong place.
+       * Clean up memory more aggressively.  This lowers the
+         maximum memory used, most typically for TLS based EAP methods.
+       * Prevent the server from unlinking the control socket of an
+         already running instance.
+       * Fallback to using the configured OCSP URL if one exists, and
+         no URL is provided in the certificate.
+       * Return CoA-NAK if proxying CoA fails.  Based on patch from
+         Jorge Pereira.
+       * Lower peak memory usage by decreasing size of internal
+         memory pools.
+       * The control socket is now left in place if a second copy
+         of the server is accidentally started.
+       * Allow virtual attributes in "switch", "case", etc.
+         Fixes #1240 and #1265.
+       * Many spell check / typo fixes in comments and example
+         configuration files.
+       * Better handle multiple DHCP listeners.
+       * Don't print secrets for old-style realms.  Fixes #1267.
+       * Don't fall through in empty "case" statements.
+         Fixes #1274.
+       * Update EAP-TTLS so that MPPE keys are correctly calculated with TLSv1.2.
+       * Always delete MS-MPPE-* from the TTLS inner tunnel. This allows
+         TTLS / EAP-MSCHAPv2 to work. Fixes #1206.
+       * Fix off by one error that caused some MSCHAP-Error messages to
+         be sent without the password change version (V=3) and the textual
+         message component (M=).
+       * Always include C= V= and M= in MSCHAPv2 errors.  RFC 2759 does not say
+         that any of these fields are optional, and not including V= caused
+         errors with wpa_supplicant.
+       * Do not include M= in MSCHAPv1 errors.  It's not supported.
+
+FreeRADIUS 3.0.9 Wed 08 Jul 2015 12:00:00 EDT urgency=medium
+       Feature improvements
+       * Make "pool" configurations more consistent, and
+         update documentation for them.
+       * Move connection pool logic to "most recently started",
+         instead of MRU.  This should help with pool stability.
+       * More VSAs for 3GPP2
+       * Added examples of multi-value attributes to rlm_perl.
+       * LDAP-Group and SQL-Group attributes are now dynamically
+         allocated.
+       * Only the "sql" module registers SQL-Group.  Other instances
+         register "instance-name-SQL-Group", similarly to "ldap".
+       * Unknown attributes are now complained about more often
+         when used in unlang statements.  e.g. if (Foo-Bar == 3)
+         used to be a string to string comparison.  It is now a
+         parse error.
+       * Rename RLM_COMPONENT_* to MOD_* in the code.
+         This makes many things easier.
+       * Move to C99 initializers for modules.
+       * Load modules in raddb/mods-enabled.  This allows attributes
+         like "LDAP-Group" to be used in the "files" module,
+         without explicit ordering or listing in "instantiate".
+       * Added 'bootstrap' section to modules.  Third-party modules
+         will need to be updated.
+       * When adding clients from a DB, add them to a virtual server
+         if that virtual server has a "listen" section.  Otherwise,
+         add the clients to the global list.
+       * When reading dynamic clients from a file, don't expire them
+         if the underlying file is unchanged.
+       * Allow the server to originate CoA requests from the post-auth
+         stage.
+       * The server creates ${run_dir} and ${logdir} in daemon mode,
+         if they do not already exist.
+       * Add dictionary for Wi-Fi Alliance Hotspot 2.0.  The server
+         now supports all mandatory and optional attributes for this
+         specification.
+       * HUP now re-loads the configuration only if the files have
+         changed.  If all files are unchanged, HUP re-opens the
+         log file, and does nothing else.
+       * Much better debug messages for EAP-TLS, including which
+         attributes are cached, and when they are retrieved.
+       * Increase default max_requests to 16384.  Memory is cheap now.
+       * Added "stats memory" commands to radmin.  Debug build only.
+       * Aptilo controller dictionary updates.
+       * SQL modules now use Acct-Unique-Session-Id everywhere.
+       * The redis modules are now stable.
+       * The LDAP module now supports SASL "interactive bind" method.
+         This allows Kerberos based administrator and user binds.
+       * DHCP code is now in libfreeradius-dhcp.
+       * More DHCP encoding / decoding unit tests.
+       * rlm_replicate can now be listed in the "accounting" section.
+       * Better sqlite debugging output.
+       * Remove "required" option from many sql_ippool directives.
+       * Set default CA "basic constraints" to "critical".  Fixes #1073
+       * Updates to help / man pages from Jorge Pereira.
+       * Added more tests.
+
+       Bug fixes
+       * Be more careful about unused config item warnings
+         when using -Xx.
+       * Move more defines to be auto-generated.
+       * Allow virtual servers in proxy fallback.
+       * Allow %{module:} to work.
+       * Don't crash in RadSec.  Closes #980.
+       * Return better errors when a unix group / user
+         is not found.
+       * Re-enable detail module "locking" parameter.
+       * Don't crash when logging replies from Status-Server packets.
+       * The couchbase module now uses "update" instead of "map",
+         for consistent with the rest of the server.  See
+         raddb/mods-available/couchbase
+       * Don't require NT-Password for MS-CHAP password changes.
+       * Be a bit more careful about decrypting MS-CHAP-MPPE-Key
+         attributes. Closes #1013.  There is no perfect fix, tho.
+       * Fix security issues with EAP-PWD.
+         See http://freeradius.org/security.html#eap-pwd-2015
+       * Fix dynamic clients read from SQL in non-debug mode
+       * MS-CHAP now allows retries (i.e. password change) when
+         passwords are expired.
+       * Allow "user=radiusd" when the server is already user
+         "radiusd"
+       * suid up/down works on non-Linux systems.  This means
+         that the control socket should have the correct
+         ownership.
+       * Fix issue which caused the server to sometimes have problems
+         when a home server was marked zombie.
+       * Fix format.pl because Perl is now more picky.
+       * Fix proxy to Packet-Dst-IP-Address, so that it uses the
+         correct destination port.
+       * Fix corner case with cursor functions and removal.
+       * OpenDirectory fixes and documentation.
+       * Fix leaks in rlm_redis.
+       * RFC 6929 "evs" attributes are now encoded / decoded
+         properly.
+       * Fix talloc pool leaks when receiving malformed or
+         retransmitted Accounting/CoA requests.
+       * Printed attributes again use double quotes instead of
+         single quotes.
+       * Set X509_V_FLAG_CRL_CHECK_ALL, and add "check_all_crl"
+         to eap.conf.  Fixes oCert CVE-2015-4680.
+       * rlm_expr now errors out correctly on malformed attribute
+         references instead of triggering an assert.
+       * Make "break" work in "foreach" loops
+       * Allow dynamic expansions to work again in the "hints" file.
+       * Correct minor typos in comments and examples from Alan Buxy.
+       * Re-urlencode the path portion of ldapi:// urls before
+         passing it to ldap_initialise.
+
+FreeRADIUS 3.0.8 Wed 22 Apr 2015 13:30:00 EDT urgency=medium
+       Feature improvements
+        * Allow syslog_severity to be set in rlm_linelog.
+       * Allow defaults to be set for bulk clients in LDAP and couchbase.
+       * Updates to dhcpclient.  Patches from Nicolas C.
+       * rlm_mschap now supports direct connections to winbind, which
+         is faster than ntlm_auth.  See raddb/mods-available/mschap.
+         Patch from Matthew Newton.
+       * Recommend /dev/urandom for TLS randomness, instead of
+         ${certdir}/random
+       * Allow TLSv1 to be disabled via "disable_tlsv1" in tls{}.
+       * Allow Expanded EAP types where vendor is 0 (IETF) and
+         type is normal EAP type.  Supplicants sending Expanded
+         EAP types like this are broken.
+       * Add support for server side sort controls when searching for
+         user objects in rlm_ldap.
+
+       Bug fixes
+       * Don't complain about "authorize" in "server {}" blocks, but
+         only if there's no "server" block.
+       * Fix cosmetic issue where debug from the first packet read by
+         a detail reader thread would be emited during config parsing.
+       * Fix ASSERT on truncated detail packets.
+       * Don't use main server log functions from within panic_action,
+         as in the case of syslog this would cause deadlocks if the
+         fault was triggered from within a malloc.
+       * Fix issue in "switch" when "correct_escapes = false".
+         Fixes #911.
+       * Fix sqlcounter configuration to use "%%b" instead of "%b",
+         otherwise the new syntax validation will fail.
+       * Allow forward references in configuration items.  Modules
+         aren't always loaded in a sane order.
+       * Fix more escaping issues.  Closes #912.
+       * Decode MAC addresses correctly for VMPS.
+       * Fix memory leak with TLS connections.
+       * Fix state machine threading issues for conflicting packets.
+       * Fix copy_request_to_tunnel issues for tagged attributes.
+       * Allow "ok" to over-ride "updated" inside of Auth-Type sections.
+       * Update state machine so that post-proxy is run though child
+         threads for performance, instead of blocking the main thread.
+       * Allow "netmask" to work again in client definitions.
+       * Relax restrictions on SQL group queries.
+       * track outgoing proxy sockets and clean them up more aggressively.
+       * track proxy statistics, including CoA and Disconnect.
+       * If radmin has a connection failure when running a command,
+         it re-connects and runs the command again.
+       * mark home servers "unknown" less aggressively.
+       * Fix potential SEGV in PostgreSQL driver on error.
+       * Fix issue where fields like nas_type would not be accessible via
+         the %{client:} xlat, for dynamic clients.
+       * Set default busy_timeout (of 200ms) in the sqlite driver, so writes
+         don't cause selects to fail in multithreaded mode. This is user
+         configurable, and may be increased if required.
+       * Convert Password-With-Header attributes to binary (from hex or
+         base64), in the authorize method of rlm_pap.
+       * Fix invalid assert in state.c, that could cause abort in
+         post-auth.
+       * Fix double free when -m flag is used, and connection pools are
+         referenced by multiple modules.
+       * RADIUS over TLS accounting uses the same port as authentication.
+       * Regularized return codes from radmin commands.
+       * Fix RHEL spec file so it works correctly for Centos7 which uses
+         systemd, and didn't like the SystemV init script.
+       * radwho and radlast now have a -D option to load dictionaries
+       * DHCP packets are no longer checked for duplicates.
+       * Don't crash in sql module group comparisons in corner case.
+       * Calculate MPPE keys correctly when using TLS 1.2.
+       * Fix load-balance sections.  Closes #945
+       * TLS certificates are available again in the post-auth section.
+         They are not available for session resumption.
+       * radclient encodes CHAP-Password properly when using -c.
+         Closes #955.
+       * Fix issue in rlm_cache_memcached driver that caused variable
+         length values to be truncated.
+       * Fix track functionality in detail reader, so it no longer
+         fails with a "Failed marking detail request as done: Bad file
+         descriptor" error.
+       * Actually add the peer identity (as User-Name) to the inner
+         tunnel in EAP-PWD requests, so it's available for lookups.
+       * Fixes to PostgreSQL queries.  Patches from Santiago Gimeno.
+
 FreeRADIUS 3.0.7 Thu 19 Feb 2015 12:00:00 EDT urgency=medium
        Feature improvements
        * Allow coa home_servers to be derived from client
@@ -812,7 +1100,7 @@ FreeRADIUS 3.0.0 Mon  7 Oct 2013 15:48:14 EDT urgency=medium
        * Added EAP-PWD implementation from Dan Harkins
        * Added connection pools for modules. This unifies connection
          management which was previously different for different modules.
-       * SQL now uses the connection pool.  See mods-available/sql
+l      * SQL now uses the connection pool.  See mods-available/sql
        * SQL now supports arbitrary Acct-Status-Types.
          These changes are not compatible with 2.x.
        * SQL now has full support for SQLite.  See raddb/sql/main/sqlite/
index 097a4e3..439aedc 100644 (file)
@@ -2,7 +2,7 @@
 
   All code in this server was written for this project.
 
-  The server is mostly compatible with livingston radiusd-2.01
+  The server is mostly compatible with Livingston radiusd-2.01
   (no menus or s/key support though) but with more features, such as:
 
     o Can limit the maximum number of simultaneous logins on a per-user basis!
     o Supports Vendor-Specific attributes
     o Supports many different plug-in modules for authentication,
       authorization, and accounting.
-    o No good documentation at all, just like the original radiusd 1.16!
 
-  Work on real manual pages is progressing slowly. For a large part you
-  can use the documentation of the Livingston 2.01 server. Just remember
-  that using Prefix and Suffix in both the "users" and the (FreeRadius
-  specific) "hints" file will give unpredictable results. Well actually
-  it will result in Prefix and Suffix probably not working in the "users"
-  file if you already stripped them off in the "hints" file.
 
 2. INSTALLATION
 
    See the INSTALL file, in the parent directory.
 
+
 3. CONFIGURATION FILES
 
   For every file there is a fully commented example file included, that
@@ -54,7 +48,7 @@
 
   Every NAS (Network Access Server, also known as terminal server) should have
   an entry in this file with an abbreviated name and the type of NAS it
-  is. Currently FreeRadius supports the following NAS types:
+  is. Currently FreeRADIUS supports the following NAS types:
 
   Terminal Server                       Type in naslist
 
   You can uses spaces in usernames by escaping them with \ or by using
   quotes. For example, "joe user" or joe\ user.
 
-  The FreeRadius server does not trim any spaces from a username received
-  from the portmaster (livingston does, in perl notation, $user =~ s/\s+.*//;)
+  The FreeRADIUS server does not trim any spaces from a username received
+  from the portmaster (Livingston does, in perl notation, $user =~ s/\s+.*//;)
 
 3g. NEW RADIUS ATTRIBUTES (to be used in the USERS file).
 
 
   For example, "Wk2305-0855,Sa,Su2305-1655".
 
-  Radiusd calculates the number of seconds left in the time span, and
+  radiusd calculates the number of seconds left in the time span, and
   sets the Session-Timeout to that number of seconds. So if someones
   Login-Time is "Al0800-1800" and she logs in at 17:30, Session-Timeout
   is set to 1800 seconds so that she is kicked off at 18:00.
 
+
 4. LOG FILES
 
-4a. /var/log/radutmp
+4a. /var/log/radius/radutmp
 
   In this file the currently logged in users are held. The program "radwho"
   reads this file and gives you a summary. Rogue sessions can be deleted
   from this file with the "radzap" program.
 
-4b. /var/log/radwtmp
+4b. /var/log/radius/radwtmp
 
   This file is "wtmp" compatible and keeps a history of all radius logins/
   logouts. This file can be read with the "last" program, and other Unix
   accounting programs (such as "ac" and "sac") can be used to produce a
   summary.
 
-4c. /var/log/radius.log
+4c. /var/log/radius/radius.log
 
-  All RADIUS informational. diagnostic and error messages are logged in
-  this file.  If radiusd has been started with the "-y" flag, all logins
-  attempts will be logged in this file. For failed logins, the wrong password
-  will also be logged.  With the "-z" flag, the passwords for successful
-  logins will be logged as well. That's pretty dangerous though in case
-  anyone unpriviliged ever manages to get access to this file!
+  All RADIUS informational, diagnostic and error messages are logged in
+  this file, including all login attempts.
 
-4d. /var/log/radacct/<terminal_server>/detail
+4d. /var/log/radius/radacct/<client_ip>/detail
 
-  This is the original radius logfile, as written by all the livingston
-  radius servers. It's only created if the directory /var/log/radacct exists.
-  The <terminal_server> name is the short name if one is defined in
-  /etc/raddb/naslist.
+  This is the original radius logfile, as written by all the Livingston
+  radius servers. It's only created if the directory
+  /var/log/radius/radacct exists.
 
   For more configuration options on the detail file please see
-  README.rlm_detail as it expands upon this greatly.
+  raddb/mods-available/detail as it expands upon this greatly.
+
 
 5.  MORE INFO, SUPPORT
 
-  We know that the documentation provided is sparse. However it is not in
-  the scope of the radius server to provide a guide as to how terminal
-  servers works and how the RADIUS protocol works and is used.
+  The latest version of FreeRADIUS is always available from
+  the git repository hosted on GitHub at
+
+    https://github.com/FreeRADIUS/freeradius-server
+
+  or see
+
+    http://freeradius.org/git/
+
+  for more information.
+
+  There are two mailing lists for users and developers. General
+  user, administrator and configuration issues should be discussed
+  on the users list at:
+
+    http://lists.freeradius.org/mailman/listinfo/freeradius-users
+
+  When asking for help on the users list, be sure the include a
+  detailed and clear description of the problem, together with
+  full debug output from FreeRADIUS, obtained by running
 
-  The latest version of FreeRadius is always available through
-  anonymous CVS from cvs.freeradius.org - for more info, see
-  <URL: http://www.freeradius.org/>
+    radiusd -X
 
-  There are two GNU Mailman mailing lists hosted by Cistron Internet Services:
-  a 'users' list, at:
+  Developers only discussion is to be had on the devel list:
 
-       http://lists.freeradius.org/pipermail/freeradius-users/
+    http://lists.freeradius.org/mailman/listinfo/freeradius-devel
 
-  and a 'developers only' list, at
+  Please do not raise general configuration issues here.
 
-       http://lists.freeradius.org/pipermail/freeradius-devel/
 
 6.  OTHER INFORMATION
 
   todo/                TODO list and assorted files.
 
 
-  If you have ANY problems, concerns, or surprises when running the
-server, then run it in debugging mode, as root, from the command line:
+  If you have ANY problems, concerns, or surprises when running
+  the server, then run it in debugging mode, as root, from the
+  command line:
 
-$ radiusd -X
+  $ radiusd -X
 
-It will produce a large number of messages.  The answers to many
-questions, and the solution to many problems, can usually be found in
-these messages.
+  It will produce a large number of messages.  The answers to many
+  questions, and the solution to many problems, can usually be found in
+  these messages.
 
   For further details, see:
 
index 0644132..6a7843f 100644 (file)
--- a/doc/bugs
+++ b/doc/bugs
@@ -1,6 +1,6 @@
 BUGS
 
-0.INTRODUCTION
+0. INTRODUCTION
 
   The FreeRADIUS web site is at <URL: http://www.freeradius.org>, and
   most information referenced in this document can be found there.
@@ -10,7 +10,8 @@ BUGS
   development list to discuss it. If you're the type who know little about
   how to code, then this is the place for you!
 
-1.YOU FOUND A BUG
+
+1. YOU FOUND A BUG
 
   Where the server terminates ungracefully due to a bus error, 
   segmentation violation, or other memory error, you should create 
@@ -31,9 +32,10 @@ BUGS
   Please make sure to READ and RESPECT the house-rules. You will get much
   better response and much faster if you do!
 
-2.CORE DUMPS
 
-  If the server, or one of the accompyaning programs core dumps, then
+2. CORE DUMPS
+
+  If the server, or one of the accompanying programs core dumps, then
   you should rebuild the server as follows:
 
   $ ./configure --enable-developer
@@ -56,6 +58,11 @@ BUGS
 
   and follow the instructions in section 4, below.
 
+  You can also enable the "panic_action" given in raddb/radiusd.conf.
+  See the comments in that file for more details about automatically
+  collecting gdb debugging information when the server crashes.
+
+
 3. DEBUGGING A LIVE SERVER
 
   If you can't get a core dump, or the problem doesn't result in a
@@ -77,7 +84,7 @@ BUGS
   (gdb) set args ...
 
   Where the "..." are the command-line arguments you normally pass to
-  radiusd.  Fo debugging, you probably want to do:
+  radiusd.  For debugging, you probably want to do:
 
   (gdb) set args -f
 
@@ -92,6 +99,7 @@ BUGS
 
   And follow the instructions in section 4, below.
 
+
 4. OBTAINING USEFUL INFORMATION
 
   Once you have a core dump loaded into gdb, or FreeRADIUS running under
@@ -125,6 +133,7 @@ BUGS
 
   You should provide the issue number in any mail sent to the user's list.
 
+
 5. VALGRIND
 
   On Linux systems, "valgrind" is a useful tool that can catch certain
@@ -138,27 +147,27 @@ $ valgrind --tool=memcheck --leak-check=full radiusd -Xm
   inside of the FreeRADIUS source should be brought to our attention.
 
 
-6. Running with "screen"
+6. RUNNING WITH "SCREEN"
 
   If the bug is a crash of the server, and it takes a long time for the
   crash to happen, perform the following steps:
 
- * log in as root
* open a screen session (http://blogamundo.net/code/screen/)
-   $ screen bash
- * make sure FreeRADIUS is not running
- * make sure you have all the debug symbols about, or a debugable
-   version of the server installed
- * configure screen to log to a file; 'Ctrl-A H'
- * type 'gdb /path/to/radius'  (or /path/to/freeradius on Debian)
- * at the (gdb) prompt, type 'run -X'
- * detach from screen 'Ctrl-A D'
- * when you notice FreeRADIUS has died, reconnect to your screen session
-   $ screen -D -r
- * at the (gdb) prompt type 'where' or for *lots* of info try
-   'thread apply all bt full'
- * tell screen to stop logging, 'Ctrl-A H'
- * logout from screen
 * log in as root
 * open a screen session (https://www.gnu.org/software/screen/)
+    $ screen bash
 * make sure FreeRADIUS is not running
 * make sure you have all the debug symbols about, or a debugable
+    version of the server installed
 * configure screen to log to a file; 'Ctrl-A H'
 * type 'gdb /path/to/radius'  (or /path/to/freeradius on Debian)
 * at the (gdb) prompt, type 'run -X'
 * detach from screen 'Ctrl-A D'
 * when you notice FreeRADIUS has died, reconnect to your screen session
+    $ screen -D -r
 * at the (gdb) prompt type 'where' or for *lots* of info try
+    'thread apply all bt full'
 * tell screen to stop logging, 'Ctrl-A H'
 * logout from screen
 
 --
 
index f478328..444696d 100644 (file)
@@ -240,5 +240,3 @@ have a really good reason for doing so.
       kinds of work into one function makes the server difficult to
       debug and maintain.
 
-      See the 'coding-methods.txt' document in this directory for
-      further description of coding methods.
index 0b792e0..b40f98c 100644 (file)
@@ -1,12 +1,14 @@
 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 
+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.
+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
 --------------
@@ -16,26 +18,32 @@ Hints and tips
 
 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."
+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.
+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.
+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.
+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 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.
+If your commit includes significant whitespace changes these should also be
+broken out into another, separate, commit.
 
 Submitting patches via GitHub
 -----------------------------
@@ -52,9 +60,10 @@ Submitting patches via email
 ~~~~~~~~~~~~
 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.
+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::
 
@@ -66,8 +75,9 @@ To create a patch for a single file, it is often sufficient to do::
    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::
+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/
 
@@ -79,34 +89,39 @@ against your own source tree. For example::
 2. Select e-mail destination
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If you are on the developers mailing list, send the patch there. freeradius-devel@lists.freeradius.org
+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.
+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.
+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.
+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.
+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.
+It is important to note, either in the subject line or in the patch
+description, the server version to which this patch applies.
index ee8a57d..2fc510e 100644 (file)
@@ -42,7 +42,7 @@ Modules perform an action when RADIUS packets are received.  Modules
 which do more (creating threads, forking programs) will NOT be added
 to the server, and the server core will NOT be modified to enable
 these kinds of modules.  Those functions more properly belong in a
-seperate application.
+separate application.
 
 Modules ARE permitted to open sockets to other network programs, and
 to send and receive data on those sockets.  Modules are NOT permitted
index 01fb59b..6e218c0 100644 (file)
@@ -7,11 +7,13 @@ Changelog with the version number and any last updates.
 vi doc/ChangeLog
 git commit doc/ChangeLog
 
+
        Change version numbers in the VERSION file:
 
 vi VERSION
 git commit VERSION
 
+
        Make the files
 
        Note that it also does "make dist-check", which checks
@@ -27,6 +29,7 @@ make dist
 
 make dist-tag
 
+
        Sign the packages.  You will need the correct GPG key for this
        to work.
 
index 0d87d01..00562de 100644 (file)
@@ -308,7 +308,7 @@ HOW DO I USE IT (FAQ/Examples)
    Once EAP-Identity response is received by the server, based on the
    default_eap_type, the server will send a new request (MD5-Challenge
    request incase of md5, TLS-START request incase of tls) to the supplicant.
-   If the supplicant is rfc2284 compliant and doesnot support the
+   If the supplicant is rfc2284 compliant and does not support the
    EAP-Type sent by the server then it sends EAP-Acknowledge with the
    supported EAP-Type. If this EAP-Type is supported by the server then it
    will send the respective EAP-request.
index 45952f7..3d2840f 100644 (file)
@@ -28,7 +28,7 @@ the same pool, and addresses do not need to be concurrent.
 
 We are currently using the variable definitions of the xlat module, so
 before editing the sqlippool.conf file, please go and read the
-variables.txt in the freeradius/doc directory. It will help you alot!..
+variables.rst in the doc/configuration directory. It will help you alot!..
 
 As you may noticed, there is a pool-key variable in the config file which
 allows you to select which attribute is unique according to your NAS setup.
diff --git a/doc/rfc/rfc7055.txt b/doc/rfc/rfc7055.txt
new file mode 100644 (file)
index 0000000..08eefbd
--- /dev/null
@@ -0,0 +1,1963 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF)                   S. Hartman, Ed.
+Request for Comments: 7055                             Painless Security
+Category: Standards Track                                     J. Howlett
+ISSN: 2070-1721                                                JANET(UK)
+                                                           December 2013
+
+
+     A GSS-API Mechanism for the Extensible Authentication Protocol
+
+Abstract
+
+   This document defines protocols, procedures, and conventions to be
+   employed by peers implementing the Generic Security Service
+   Application Program Interface (GSS-API) when using the Extensible
+   Authentication Protocol mechanism.  Through the GS2 family of
+   mechanisms defined in RFC 5801, these protocols also define how
+   Simple Authentication and Security Layer (SASL) applications use the
+   Extensible Authentication Protocol.
+
+Status of This Memo
+
+   This is an Internet Standards Track document.
+
+   This document is a product of the Internet Engineering Task Force
+   (IETF).  It represents the consensus of the IETF community.  It has
+   received public review and has been approved for publication by the
+   Internet Engineering Steering Group (IESG).  Further information on
+   Internet Standards is available in Section 2 of RFC 5741.
+
+   Information about the current status of this document, any errata,
+   and how to provide feedback on it may be obtained at
+   http://www.rfc-editor.org/info/rfc7055.
+
+Copyright Notice
+
+   Copyright (c) 2013 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 1]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+Table of Contents
+
+   1. Introduction ....................................................3
+      1.1. Discovery ..................................................4
+      1.2. Authentication .............................................4
+      1.3. Secure Association Protocol ................................6
+   2. Requirements Notation ...........................................6
+   3. EAP Channel Binding and Naming ..................................6
+      3.1. Mechanism Name Format ......................................7
+      3.2. Internationalization of Names .............................10
+      3.3. Exported Mechanism Names ..................................10
+      3.4. Acceptor Name RADIUS AVP ..................................11
+      3.5. Proxy Verification of Acceptor Name .......................11
+   4. Selection of EAP Method ........................................12
+   5. Context Tokens .................................................13
+      5.1. Mechanisms and Encryption Types ...........................14
+      5.2. Processing Received Tokens ................................15
+      5.3. Error Subtokens ...........................................16
+      5.4. Initial State .............................................16
+           5.4.1. Vendor Subtoken ....................................17
+           5.4.2. Acceptor Name Request ..............................17
+           5.4.3. Acceptor Name Response .............................18
+      5.5. Authenticate State ........................................18
+           5.5.1. EAP Request Subtoken ...............................19
+           5.5.2. EAP Response Subtoken ..............................19
+      5.6. Extensions State ..........................................20
+           5.6.1. Flags Subtoken .....................................20
+           5.6.2. GSS Channel Bindings Subtoken ......................20
+           5.6.3. MIC Subtoken .......................................21
+      5.7. Example Token .............................................22
+      5.8. Context Options ...........................................23
+   6. Acceptor Services ..............................................23
+      6.1. GSS-API Channel Binding ...................................24
+      6.2. Per-Message Security ......................................24
+      6.3. Pseudorandom Function .....................................24
+   7. IANA Considerations ............................................25
+      7.1. OID Registry ..............................................25
+      7.2. RFC 4121 Token Identifiers ................................26
+      7.3. GSS-EAP Subtoken Types ....................................26
+      7.4. RADIUS Attribute Assignments ..............................27
+      7.5. Registration of the EAP-AES128 SASL Mechanisms ............28
+      7.6. GSS-EAP Errors ............................................28
+      7.7. GSS-EAP Context Flags .....................................30
+   8. Security Considerations ........................................30
+   9. Acknowledgements ...............................................32
+   10. References ....................................................32
+   Appendix A. Pre-publication RADIUS VSA ............................33
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 2]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+1.  Introduction
+
+   The Application Bridging for Federated Access Beyond Web (ABFAB)
+   document [ABFAB-ARCH] describes an architecture for providing
+   federated access management to applications using the Generic
+   Security Service Application Programming Interface (GSS-API)
+   [RFC2743] and Simple Authentication and Security Layer (SASL)
+   [RFC4422].  This specification provides the core mechanism for
+   bringing federated authentication to these applications.
+
+   The Extensible Authentication Protocol (EAP) [RFC3748] defines a
+   framework for authenticating a network access client and server in
+   order to gain access to a network.  A variety of different EAP
+   methods are in wide use; one of EAP's strengths is that for most
+   types of credentials in common use, there is an EAP method that
+   permits the credential to be used.
+
+   EAP is often used in conjunction with a backend Authentication,
+   Authorization and Accounting (AAA) server via RADIUS [RFC3579] or
+   Diameter [RFC4072].  In this mode, the Network Access Server (NAS)
+   simply tunnels EAP packets over the backend authentication protocol
+   to a home EAP/AAA server for the client.  After EAP succeeds, the
+   backend authentication protocol is used to communicate key material
+   to the NAS.  In this mode, the NAS need not be aware of or have any
+   specific support for the EAP method used between the client and the
+   home EAP server.  The client and EAP server share a credential that
+   depends on the EAP method; the NAS and AAA server share a credential
+   based on the backend authentication protocol in use.  The backend
+   authentication server acts as a trusted third party, enabling network
+   access even though the client and NAS may not actually share any
+   common authentication methods.  As described in the architecture
+   document [ABFAB-ARCH], using AAA proxies, this mode can be extended
+   beyond one organization to provide federated authentication for
+   network access.
+
+   The GSS-API provides a generic framework for applications to use
+   security services including authentication and per-message data
+   security.  Between protocols that support GSS-API directly or
+   protocols that support SASL [RFC4422], many application protocols can
+   use GSS-API for security services.  However, with the exception of
+   Kerberos [RFC4121], few GSS-API mechanisms are in wide use on the
+   Internet.  While GSS-API permits an application to be written
+   independent of the specific GSS-API mechanism in use, there is no
+   facility to separate the server from the implementation of the
+   mechanism as there is with EAP and backend authentication servers.
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 3]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   The goal of this specification is to combine GSS-API's support for
+   application protocols with EAP/AAA's support for common credential
+   types and for authenticating to a server without requiring that
+   server to specifically support the authentication method in use.  In
+   addition, this specification supports the architectural goal of
+   transporting attributes about subjects to relying parties.  Together
+   this combination will provide federated authentication and
+   authorization for GSS-API applications.  This specification meets the
+   applicability requirements for EAP to application authentication
+   [RFC7057].
+
+   This mechanism is a GSS-API mechanism that encapsulates an EAP
+   conversation.  From the perspective of RFC 3748, this specification
+   defines a new lower-layer protocol for EAP.  From the perspective of
+   the application, this specification defines a new GSS-API mechanism.
+
+   Section 1.3 of [RFC5247] outlines the typical conversation between
+   EAP peers where an EAP key is derived:
+
+   Phase 0: Discovery
+   Phase 1: Authentication
+            1a: EAP authentication
+            1b: AAA Key Transport (optional)
+   Phase 2: Secure Association Protocol
+            2a: Unicast Secure Association
+            2b: Multicast Secure Association (optional)
+
+1.1.  Discovery
+
+   GSS-API peers discover each other and discover support for GSS-API in
+   an application-dependent mechanism.  SASL [RFC4422] describes how
+   discovery of a particular SASL mechanism such as a GSS-API EAP
+   mechanism is conducted.  The Simple and Protected Negotiation
+   mechanism (SPNEGO) [RFC4178] provides another approach for
+   discovering what GSS-API mechanisms are available.  The specific
+   approach used for discovery is out of scope for this mechanism.
+
+1.2.  Authentication
+
+   GSS-API authenticates a party called the "GSS-API initiator" to the
+   GSS-API acceptor, optionally providing authentication of the acceptor
+   to the initiator.  Authentication starts with a mechanism-specific
+   message called a "context token" sent from the initiator to the
+   acceptor.  The acceptor responds, followed by the initiator, and so
+   on until authentication succeeds or fails.  GSS-API context tokens
+   are reliably delivered by the application using GSS-API.  The
+   application is responsible for in-order delivery and retransmission.
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 4]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   EAP authenticates a party called a "peer" to a party called the "EAP
+   server".  A third party called an "EAP pass-through authenticator"
+   may decapsulate EAP messages from a lower layer and re-encapsulate
+   them into a AAA protocol.  The term EAP authenticator refers to
+   whichever of the pass-through authenticator or EAP server receives
+   the lower-layer EAP packets.  The first EAP message travels from the
+   authenticator to the peer; a GSS-API message is sent from the
+   initiator to acceptor to prompt the authenticator to send the first
+   EAP message.  The EAP peer maps onto the GSS-API initiator.  The role
+   of the GSS-API acceptor is split between the EAP authenticator and
+   the EAP server.  When these two entities are combined, the division
+   resembles GSS-API acceptors in other mechanisms.  When a more typical
+   deployment is used and there is a pass-through authenticator, most
+   context establishment takes place on the EAP server and per-message
+   operations take place on the authenticator.  EAP messages from the
+   peer to the authenticator are called responses; messages from the
+   authenticator to the peer are called requests.
+
+   Because GSS-API applications provide guaranteed delivery of context
+   tokens, the EAP retransmission timeout MUST be infinite and the EAP
+   layer MUST NOT retransmit a message.
+
+   This specification permits a GSS-API acceptor to hand off the
+   processing of the EAP packets to a remote EAP server by using AAA
+   protocols such as RADIUS, Transport Layer Security (TLS) Encryption
+   thereof [RFC6929], or Diameter.  In this case, the GSS-API acceptor
+   acts as an EAP pass-through authenticator.  The pass-through
+   authenticator is responsible for retransmitting AAA messages if a
+   response is not received from the AAA server.  If a response cannot
+   be received, then the authenticator generates an error at the GSS-API
+   level.  If EAP authentication is successful, and where the chosen EAP
+   method supports key derivation, EAP keying material may also be
+   derived.  If a AAA protocol is used, this can also be used to
+   replicate the EAP Key from the EAP server to the EAP authenticator.
+
+   See Section 5 for details of the authentication exchange.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 5]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+1.3.  Secure Association Protocol
+
+   After authentication succeeds, GSS-API provides a number of per-
+   message security services that can be used:
+
+      GSS_Wrap() provides integrity and optional confidentiality for a
+      message.
+
+      GSS_GetMIC() provides integrity protection for data sent
+      independently of the GSS-API
+
+      GSS_Pseudo_random [RFC4401] provides key derivation functionality.
+
+   These services perform a function similar to secure association
+   protocols in network access.  Like secure association protocols,
+   these services need to be performed near the authenticator/acceptor
+   even when a AAA protocol is used to separate the authenticator from
+   the EAP server.  The key used for these per-message services is
+   derived from the EAP key; the EAP peer and authenticator derive this
+   key as a result of a successful EAP authentication.  In the case that
+   the EAP authenticator is acting as a pass-through, it obtains it via
+   the AAA protocol.  See Section 6 for details.
+
+2.  Requirements Notation
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in [RFC2119].
+
+3.  EAP Channel Binding and Naming
+
+   EAP authenticates a user to a realm.  The peer knows that it has
+   exchanged authentication with an EAP server in a given realm.  Today,
+   the peer does not typically know which NAS it is talking to securely.
+   That is often fine for network access.  However, privileges to
+   delegate to a chat server seem very different than privileges for a
+   file server or trading site.  Also, an EAP peer knows the identity of
+   the home realm, but perhaps not even the visited realm.
+
+   In contrast, GSS-API takes a name for both the initiator and acceptor
+   as inputs to the authentication process.  When mutual authentication
+   is used, both parties are authenticated.  The granularity of these
+   names is somewhat mechanism dependent.  In the case of the Kerberos
+   mechanism, the acceptor name typically identifies both the protocol
+   in use (such as IMAP) and the specific instance of the service being
+   connected to.  The acceptor name almost always identifies the
+   administrative domain providing service.
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 6]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   A GSS-API EAP mechanism needs to provide GSS-API naming semantics in
+   order to work with existing GSS-API applications.  EAP channel
+   binding [RFC6677] is used to provide GSS-API naming semantics.
+   Channel binding sends a set of attributes from the peer to the EAP
+   server either as part of the EAP conversation or as part of a secure
+   association protocol.  In addition, attributes are sent in the
+   backend authentication protocol from the authenticator to the EAP
+   server.  The EAP server confirms the consistency of these attributes.
+   Confirming attribute consistency also involves checking consistency
+   against a local policy database as discussed in Section 3.5.  In
+   particular, the peer sends the name of the acceptor it is
+   authenticating to as part of channel binding.  The acceptor sends its
+   full name as part of the backend authentication protocol.  The EAP
+   server confirms consistency of the names.
+
+   EAP channel binding is easily confused with a facility in GSS-API
+   also called "channel binding".  GSS-API channel binding provides
+   protection against man-in-the-middle attacks when GSS-API is used as
+   authentication inside some tunnel; it is similar to a facility called
+   "cryptographic binding" in EAP.  See [RFC5056] for a discussion of
+   the differences between these two facilities and Section 6.1 for how
+   GSS-API channel binding is handled in this mechanism.
+
+3.1.  Mechanism Name Format
+
+   Before discussing how the initiator and acceptor names are validated
+   in the AAA infrastructure, it is necessary to discuss what composes a
+   name for an EAP GSS-API mechanism.  GSS-API permits several types of
+   generic names to be imported using GSS_Import_name().  Once a
+   mechanism is chosen, these names are converted into a mechanism-
+   specific name called a "Mechanism Name".  Note that a Mechanism Name
+   is the name of an initiator or acceptor, not of a GSS-API mechanism.
+   This section first discusses the mechanism name form and then
+   discusses what name forms are supported.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 7]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   The string representation of the GSS-EAP mechanism name has the
+   following ABNF [RFC5234] representation:
+
+        char-normal = %x00-2E/%x30-3F/%x41-5B/%x5D-FF
+        char-escaped = "\" %x2F / "\" %x40 / "\" %x5C
+        name-char = char-normal / char-escaped
+        name-string = 1*name-char
+        user-or-service = name-string
+        host = [name-string]
+        realm = name-string
+        service-specific = name-string
+        service-specifics = service-specific 0*("/" service-specifics)
+        name = user-or-service ["/" host [ "/" service-specifics]] [ "@"
+                realm ]
+
+   Special characters appearing in a name can be backslash escaped to
+   avoid their special meanings.  For example, "\\" represents a literal
+   backslash.  This escaping mechanism is a property of the string
+   representation; if the components of a name are transported in some
+   mechanism that will keep them separate without backslash escaping,
+   then backslash SHOULD have no special meaning.
+
+   The user-or-service component is similar to the portion of a network
+   access identifier (NAI) before the '@' symbol for initiator names and
+   the service name from the registry of GSS-API host-based services in
+   the case of acceptor names [GSS-IANA].  The NAI specification
+   provides rules for encoding and string preparation in order to
+   support internationalization of NAIs; implementations of this
+   mechanism MUST NOT prepare the user-or-service according to these
+   rules; see Section 3.2 for internationalization of this mechanism.
+   The host portion is empty for initiators and typically contains the
+   domain name of the system on which an acceptor service is running.
+   Some services MAY require additional parameters to distinguish the
+   entity being authenticated against.  Such parameters are encoded in
+   the service-specifics portion of the name.  The EAP server MUST
+   reject authentication of any acceptor name that has a non-empty
+   service-specifics component unless the EAP server understands the
+   service-specifics and authenticates them.  The interpretation of the
+   service-specifics is scoped by the user-or-service portion.  The
+   realm is similar to the realm portion of a NAI for initiator names;
+   again the NAI specification's internationalization rules MUST NOT be
+   applied to the realm.  The realm is the administrative realm of a
+   service for an acceptor name.
+
+   The string representation of this name form is designed to be
+   generally compatible with the string representation of Kerberos names
+   defined in [RFC1964].
+
+
+
+
+Hartman & Howlett            Standards Track                    [Page 8]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   The GSS_C_NT_USER_NAME form represents the name of an individual
+   user.  From the standpoint of this mechanism, it may take the form of
+   either an undecorated user name or a name semantically similar to a
+   network access identifier (NAI) [RFC4282].  The name is split at the
+   first at-sign ('@') into the part preceding the realm, which is the
+   user-or-service portion of the mechanism name, and the realm portion,
+   which is the realm portion of the mechanism name.
+
+   The GSS_C_NT_HOSTBASED_SERVICE name form represents a service running
+   on a host; it is textually represented as "service@host".  This name
+   form is required by most SASL profiles and is used by many existing
+   applications that use the Kerberos GSS-API mechanism.  While support
+   for this name form is critical, it presents an interesting challenge
+   in terms of EAP channel binding.  Consider a case where the server
+   communicates with a "server proxy," or a AAA server near the server.
+   That server proxy communicates with the EAP server.  The EAP server
+   and server proxy are in different administrative realms.  The server
+   proxy is in a position to verify that the request comes from the
+   indicated host.  However, the EAP server cannot make this
+   determination directly.  So, the EAP server needs to determine
+   whether to trust the server proxy to verify the host portion of the
+   acceptor name.  This trust decision depends both on the host name and
+   the realm of the server proxy.  In effect, the EAP server decides
+   whether to trust that the realm of the server proxy is the right
+   realm for the given hostname and then makes a trust decision about
+   the server proxy itself.  The same problem appears in Kerberos:
+   there, clients decide what Kerberos realm to trust for a given
+   hostname.  The service portion of this name is imported into the
+   user-or-service portion of the mechanism name; the host portion is
+   imported into the host portion of the mechanism name.  The realm
+   portion is empty.  However, authentication will typically fail unless
+   some AAA component indicates the realm to the EAP server.  If the
+   application server knows its realm, then it should be indicated in
+   the outgoing AAA request.  Otherwise, a proxy SHOULD add the realm.
+   An alternate form of this name type MAY be used on acceptors; in this
+   case, the name form is "service" with no host component.  This is
+   imported with the service as user-or-service and an empty host and
+   realm portion.  This form is useful when a service is unsure which
+   name an initiator knows it by.
+
+   If the null name type or the GSS_EAP_NT_EAP_NAME (OID
+   1.3.6.1.5.5.15.2.1) (see Section 7.1 ) is imported, then the string
+   representation above should be directly imported.  Mechanisms MAY
+   support the GSS_KRB5_NT_KRB5_PRINCIPAL_NAME name form with the OID
+   {iso(1) member-body(2) United States(840) mit(113554) infosys(1)
+   gssapi(2) krb5(2) krb5_name(1)}.  In many circumstances, Kerberos
+   GSS-API mechanism names will behave as expected when used with the
+   GSS-API EAP mechanism, but there are some differences that may cause
+
+
+
+Hartman & Howlett            Standards Track                    [Page 9]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   some confusion.  If an implementation does support importing Kerberos
+   names it SHOULD fail the import if the Kerberos name is not
+   syntactically a valid GSS-API EAP mechanism name as defined in this
+   section.
+
+3.2.  Internationalization of Names
+
+   For the most part, GSS-EAP names are transported in other protocols;
+   those protocols define the internationalization semantics.  For
+   example, if a AAA server wishes to communicate the user-or-service
+   portion of the initiator name to an acceptor, it does so using
+   existing mechanisms in the AAA protocol.  Existing
+   internationalization rules are applied.  Similarly, within an
+   application, existing specifications such as [RFC5178] define the
+   encoding of names that are imported and displayed with the GSS-API.
+
+   This mechanism does introduce a few cases where name components are
+   sent.  In these cases, the encoding of the string is UTF-8.  Senders
+   SHOULD NOT normalize or map strings before sending.  These strings
+   include RADIUS attributes introduced in Section 3.4.
+
+   When comparing the host portion of a GSS-EAP acceptor name supplied
+   in EAP channel binding by a peer to that supplied by an acceptor, EAP
+   servers SHOULD prepare the host portion according to [RFC5891] prior
+   to comparison.  Applications MAY prepare domain names prior to
+   importing them into this mechanism.
+
+3.3.  Exported Mechanism Names
+
+   GSS-API provides the GSS_Export_name call.  This call can be used to
+   export the binary representation of a name.  This name form can be
+   stored on access control lists for binary comparison.
+
+   The exported name token MUST use the format described in Section 3.2
+   of RFC 2743.  The mechanism specific portion of this name token is
+   the string format of the mechanism name described in Section 3.1.
+
+   RFC 2744 [RFC2744] places the requirement that the result of
+   importing a name, canonicalizing it to a Mechanism Name and then
+   exporting it needs to be the same as importing that name, obtaining
+   credentials for that principal, initiating a context with those
+   credentials and exporting the name on the acceptor.  In practice, GSS
+   mechanisms often, but not always, meet this requirement.  For names
+   expected to be used as initiator names, this requirement is met.
+   However, permitting empty host and realm components when importing
+   host-based services may make it possible for an imported name to
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 10]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   differ from the exported name actually used.  Other mechanisms such
+   as Kerberos have similar situations where imported and exported names
+   may differ.
+
+3.4.  Acceptor Name RADIUS AVP
+
+   See Section 7.4 for registrations of RADIUS attribute types to carry
+   the acceptor service name.  All the attribute types registered in
+   that section are strings.  See Section 3.1 for details of the values
+   in a name.
+
+   If RADIUS is used as a AAA transport, the acceptor MUST send the
+   acceptor name in these attribute types.  That is, the acceptor
+   decomposes its name and sends any non-empty portion as a RADIUS
+   attribute.  With the exception of the service-specifics portion of
+   the name, the backslash escaping mechanism is not used in RADIUS
+   attributes; backslash has no special meaning.  In the service-
+   specifics portion, a literal "/" separates components.  In this one
+   attribute, "\/" indicates a slash character that does not separate
+   components and "\\" indicates a literal backslash character.
+
+   The initiator MUST require that the EAP method in use support channel
+   binding and MUST send the acceptor name as part of the channel
+   binding data.  The client MUST NOT indicate mutual authentication in
+   the result of GSS_Init_sec_context unless all name elements that the
+   client supplied are in a successful channel binding response.  For
+   example, if the client supplied a hostname in channel binding data,
+   the hostname MUST be in a successful channel binding response.
+
+   If an empty target name is supplied to GSS_Init_sec_context, the
+   initiator MUST fail context establishment unless the acceptor
+   supplies the acceptor name response (Section 5.4.3).  If a null
+   target name is supplied, the initiator MUST use this response to
+   populate EAP channel bindings.
+
+3.5.  Proxy Verification of Acceptor Name
+
+   Proxies may play a role in verification of the acceptor identity.
+   For example, a AAA proxy near the acceptor may be in a position to
+   verify the acceptor hostname, while the EAP server is likely to be
+   too distant to reliably verify this on its own.
+
+   The EAP server or some proxy trusted by the EAP server is likely to
+   be in a position to verify the acceptor realm.  In effect, this proxy
+   is confirming that the right AAA credential is used for the claimed
+   realm and thus that the acceptor is in the organization it claims to
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 11]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   be part of.  This proxy is also typically trusted by the EAP server
+   to make sure that the hostname claimed by the acceptor is a
+   reasonable hostname for the realm of the acceptor.
+
+   A proxy close to the EAP server is unlikely to be in a position to
+   confirm that the acceptor is claiming the correct hostname.  Instead,
+   this is typically delegated to a proxy near the acceptor.  That proxy
+   is typically expected to verify the acceptor hostname and to verify
+   the appropriate AAA credential for that host is used.  Such a proxy
+   may insert the acceptor realm if it is absent, permitting realm
+   configuration to be at the proxy boundary rather than on acceptors.
+
+   Ultimately, specific proxy behavior is a matter for deployment.  The
+   EAP server MUST assure that the appropriate validation has been done
+   before including acceptor name attributes in a successful channel
+   binding response.  If the acceptor service is included, the EAP
+   server asserts that the service is plausible for the acceptor.  If
+   the acceptor hostname is included, the EAP server asserts that the
+   acceptor hostname is verified.  If the realm is included the EAP
+   server asserts that the realm has been verified, and if the hostname
+   was also included, that the realm and hostname are consistent.  Part
+   of this verification MAY be delegated to proxies, but the EAP server
+   configuration MUST guarantee that the combination of proxies meets
+   these requirements.  Typically, such delegation will involve business
+   or operational measures such as cross-organizational agreements as
+   well as technical measures.
+
+   It is likely that future technical work will be needed to communicate
+   what verification has been done by proxies along the path.  Such
+   technical measures will not release the EAP server from its
+   responsibility to decide whether proxies on the path should be
+   trusted to perform checks delegated to them.  However, technical
+   measures could prevent misconfigurations and help to support diverse
+   environments.
+
+4.  Selection of EAP Method
+
+   EAP does not provide a facility for an EAP server to advertise what
+   methods are available to a peer.  Instead, a server starts with its
+   preferred method selection.  If the peer does not accept that method,
+   the peer sends a NAK response containing the list of methods
+   supported by the client.
+
+   Providing multiple facilities to negotiate which security mechanism
+   to use is undesirable.  Section 7.3 of [RFC4462]describes the problem
+   referencing the Secure Shell (SSH) Protocol key exchange negotiation
+   and the SPNEGO GSS-API mechanism.  If a client preferred an EAP
+   method A, a non-EAP authentication mechanism B, and then an EAP
+
+
+
+Hartman & Howlett            Standards Track                   [Page 12]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   method C, then the client would have to commit to using EAP before
+   learning whether A is actually supported.  Such a client might end up
+   using C when B is available.
+
+   The standard solution to this problem is to perform all the
+   negotiation at one layer.  In this case, rather than defining a
+   single GSS-API mechanism, a family of mechanisms should be defined.
+   Each mechanism corresponds to an EAP method.  The EAP method type
+   should be part of the GSS-API OID.  Then, a GSS-API rather than EAP
+   facility can be used for negotiation.
+
+   Unfortunately, using a family of mechanisms has a number of problems.
+   First, GSS-API assumes that both the initiator and acceptor know the
+   entire set of mechanisms that are available.  Some negotiation
+   mechanisms are driven by the client; others are driven by the server.
+   With EAP GSS-API, the acceptor does not know what methods the EAP
+   server implements.  The EAP server that is used depends on the
+   identity of the client.  The best solution so far is to accept the
+   disadvantages of multi-layer negotiation and commit to using EAP GSS-
+   API before a specific EAP method.  This has two main disadvantages.
+   First, authentication may fail when other methods might allow
+   authentication to succeed.  Second, a non-optimal security mechanism
+   may be chosen.
+
+5.  Context Tokens
+
+   All context establishment tokens emitted by the EAP mechanism SHALL
+   have the framing described in Section 3.1 of [RFC2743], as
+   illustrated by the following pseudo-ASN.1 structures:
+
+   GSS-API DEFINITIONS ::=
+            BEGIN
+
+            MechType ::= OBJECT IDENTIFIER
+            -- representing EAP mechanism
+            GSSAPI-Token ::=
+            -- option indication (delegation, etc.) indicated within
+            -- mechanism-specific token
+            [APPLICATION 0] IMPLICIT SEQUENCE {
+                    thisMech MechType,
+                    innerToken ANY DEFINED BY thisMech
+                       -- contents mechanism-specific
+                       -- ASN.1 structure not required
+                    }
+            END
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 13]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   The innerToken field starts with a 16-bit network byte order token
+   type identifier.  The remainder of the innerToken field is a set of
+   type-length-value subtokens.  The following figure describes the
+   structure of the inner token:
+
+              +----------------+---------------------------+
+              | Octet Position | Description               |
+              +----------------+---------------------------+
+              | 0..1           | token ID                  |
+              |                |                           |
+              | 2..5           | first subtoken type       |
+              |                |                           |
+              | 6..9           | length  of first subtoken |
+              |                |                           |
+              | 10..10+n-1     | first subtoken body       |
+              |                |                           |
+              | 10+n..10+n+3   | second subtoken type      |
+              +----------------+---------------------------+
+
+                         Structure of Inner Token
+
+   The inner token continues with length, second subtoken body, and so
+   forth.  If a subtoken type is present, its length and body MUST be
+   present.
+
+   The length is a four-octet length of the subtoken body in network
+   byte order.  The length does not include the length of the type field
+   or the length field; the length only covers the body.
+
+   Tokens from the initiator to acceptor use an inner token type with ID
+   06 01; tokens from acceptor to initiator use an inner token type with
+   ID 06 02.  These token types are registered in the registry of RFC
+   4121 token types; see Section 7.2.
+
+   See Section 5.7 for the encoding of a complete token.  The following
+   sections discuss how mechanism OIDs are chosen and the state machine
+   that defines what subtokens are permitted at each point in the
+   context establishment process.
+
+5.1.  Mechanisms and Encryption Types
+
+   This mechanism family uses the security services of the Kerberos
+   cryptographic framework [RFC3961].  The root of the OID ARC for
+   mechanisms described in this document is 1.3.6.1.5.5.15.1.1; a
+   Kerberos encryption type number [RFC3961] is appended to that root
+   OID to form a mechanism OID.  As such, a particular encryption type
+   needs to be chosen.  By convention, there is a single object
+   identifier arc for the EAP family of GSS-API mechanisms.  A specific
+
+
+
+Hartman & Howlett            Standards Track                   [Page 14]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   mechanism is chosen by adding the numeric Kerberos encryption type
+   number to the root of this arc.  However, in order to register the
+   SASL name, the specific usage with a given encryption type needs to
+   be registered.  This document defines the EAP-AES128 GSS-API
+   mechanism.
+
+5.2.  Processing Received Tokens
+
+   Whenever a context token is received, the receiver performs the
+   following checks.  First, the receiver confirms the object identifier
+   is that of the mechanism being used.  The receiver confirms that the
+   token type corresponds to the role of the peer: acceptors will only
+   process initiator tokens and initiators will only process acceptor
+   tokens.
+
+   Implementations of this mechanism maintain a state machine for the
+   context establishment process.  Both the initiator and acceptor start
+   out in the initial state; see Section 5.4 for a description of this
+   state.  Associated with each state are a set of subtoken types that
+   are processed in that state and rules for processing these subtoken
+   types.  The receiver examines the subtokens in order, processing any
+   that are appropriate for the current state.  Unknown subtokens or
+   subtokens that are not expected in the current state are ignored if
+   their critical bit (see below) is clear.
+
+   A state may have a set of required subtoken types.  If a subtoken
+   type is required by the current state but no subtoken of that type is
+   present, then the context establishment MUST fail.
+
+   The most significant bit (0x80000000) in a subtoken type is the
+   critical bit.  If a subtoken with this bit set in the type is
+   received, the receiver MUST fail context establishment unless the
+   subtoken is understood and processed for the current state.
+
+   The subtoken type MUST be unique within a given token.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 15]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+5.3.  Error Subtokens
+
+   The acceptor may always end the exchange by generating an error
+   subtoken.  The error subtoken has the following format:
+
+   +--------+----------------------------------------------------------+
+   | Pos    | Description                                              |
+   +--------+----------------------------------------------------------+
+   | 0..3   | 0x80 00 00 01                                            |
+   |        |                                                          |
+   | 4..7   | length of error token                                    |
+   |        |                                                          |
+   | 8..11  | major status from RFC 2744 as 32-bit network byte order  |
+   |        |                                                          |
+   | 12..15 | GSS-EAP error code as 32-bit network byte order; see     |
+   |        | Section 7.6                                              |
+   +--------+----------------------------------------------------------+
+
+   Initiators MUST ignore octets beyond the GSS-EAP error code for
+   future extensibility.  As indicated, the error token is always marked
+   critical.
+
+5.4.  Initial State
+
+   Both the acceptor and initiator start the context establishment
+   process in the initial state.
+
+   The initiator sends a token to the acceptor.  It MAY be empty; no
+   subtokens are required in this state.  Alternatively, the initiator
+   MAY include a vendor ID subtoken or an acceptor name request
+   subtoken.
+
+   The acceptor responds to this message.  It MAY include an acceptor
+   name response subtoken.  It MUST include a first EAP request; this is
+   an EAP request/identity message (see Section 5.5.1 for the format of
+   this subtoken).
+
+   The initiator and acceptor then transition to authenticate state.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 16]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+5.4.1.  Vendor Subtoken
+
+   The vendor ID subtoken has type 0x0000000B and the following
+   structure:
+
+                 +-------------+------------------------+
+                 | Pos         | Description            |
+                 +-------------+------------------------+
+                 | 0..3        | 0x0000000B             |
+                 |             |                        |
+                 | 4..7        | length of vendor token |
+                 |             |                        |
+                 | 8..8+length | Vendor ID string       |
+                 +-------------+------------------------+
+
+   The vendor ID string is an UTF-8 string describing the vendor of this
+   implementation.  This string is unstructured and for debugging
+   purposes only.
+
+5.4.2.  Acceptor Name Request
+
+   The acceptor name request token is sent from the initiator to the
+   acceptor indicating that the initiator wishes a particular acceptor
+   name.  This is similar to Transport Layer Security (TLS) Server Name
+   Indication [RFC6066] that permits a client to indicate which one of a
+   number of virtual services to contact.  The structure is as follows:
+
+                  +------+------------------------------+
+                  | Pos  | Description                  |
+                  +------+------------------------------+
+                  | 0..3 | 0x00000002                   |
+                  |      |                              |
+                  | 4..7 | length of subtoken           |
+                  |      |                              |
+                  | 8..n | string form of acceptor name |
+                  +------+------------------------------+
+
+   It is likely that channel binding and thus authentication will fail
+   if the acceptor does not choose a name that is a superset of this
+   name.  That is, if a hostname is sent, the acceptor needs to be
+   willing to accept this hostname.
+
+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 17]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+5.4.3.  Acceptor Name Response
+
+   The acceptor name response subtoken indicates what acceptor name is
+   used.  This is useful, for example, if the initiator supplied no
+   target name to the context initialization.  This allows the initiator
+   to learn the acceptor name.  EAP channel bindings will provide
+   confirmation that the acceptor is accurately naming itself.
+
+   This token is sent from the acceptor to initiator.  In the Initial
+   state, this token would typically be sent if the acceptor name
+   request is absent, because if the initiator already sent an acceptor
+   name, then the initiator knows what acceptor it wishes to contact.
+   This subtoken is also sent in Extensions state Section 5.6, so the
+   initiator can protect against a man-in-the-middle modifying the
+   acceptor name request subtoken.
+
+                  +------+------------------------------+
+                  | Pos  | Description                  |
+                  +------+------------------------------+
+                  | 0..3 | 0x00000003                   |
+                  |      |                              |
+                  | 4..7 | length of subtoken           |
+                  |      |                              |
+                  | 8..n | string form of acceptor name |
+                  +------+------------------------------+
+
+5.5.  Authenticate State
+
+   In this state, the acceptor sends EAP requests to the initiator and
+   the initiator generates EAP responses.  The goal of the state is to
+   perform a successful EAP authentication.  Since the acceptor sends an
+   identity request at the end of the initial state, the first half-
+   round-trip in this state is a response to that request from the
+   initiator.
+
+   The EAP conversation can end in a number of ways:
+
+   o  If the EAP state machine generates an EAP Success message, then
+      the EAP authenticator believes the authentication is successful.
+      The acceptor MUST confirm that a key has been derived
+      (Section 7.10 of [RFC3748]).  The acceptor MUST confirm that this
+      success indication is consistent with any protected result
+      indication for combined authenticators and with AAA indication of
+      success for pass-through authenticators.  If any of these checks
+      fail, the acceptor MUST send an error subtoken and fail the
+      context establishment.  If these checks succeed, the acceptor
+      sends the Success message using the EAP Request subtoken type and
+      transitions to Extensions state.  If the initiator receives an EAP
+
+
+
+Hartman & Howlett            Standards Track                   [Page 18]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+      Success message, it confirms that a key has been derived and that
+      the EAP Success is consistent with any protected result
+      indication.  If so, it transitions to Extensions state.
+      Otherwise, it returns an error to the caller of
+      GSS_Init_sec_context without producing an output token.
+
+   o  If the acceptor receives an EAP failure, then the acceptor sends
+      this in the EAP Request subtoken type.  If the initiator receives
+      an EAP Failure, it returns GSS failure.
+
+   o  If there is some other error, the acceptor MAY return an error
+      subtoken.
+
+5.5.1.  EAP Request Subtoken
+
+   The EAP Request subtoken is sent from the acceptor to the initiator.
+   This subtoken is always critical and is REQUIRED in the
+   authentication state.
+
+                  +-------------+-----------------------+
+                  | Pos         | Description           |
+                  +-------------+-----------------------+
+                  | 0..3        | 0x80000005            |
+                  |             |                       |
+                  | 4..7        | length of EAP message |
+                  |             |                       |
+                  | 8..8+length | EAP message           |
+                  +-------------+-----------------------+
+
+5.5.2.  EAP Response Subtoken
+
+   This subtoken is REQUIRED in authentication state messages from the
+   initiator to the acceptor.  It is always critical.
+
+                  +-------------+-----------------------+
+                  | Pos         | Description           |
+                  +-------------+-----------------------+
+                  | 0..3        | 0x80000004            |
+                  |             |                       |
+                  | 4..7        | length of EAP message |
+                  |             |                       |
+                  | 8..8+length | EAP message           |
+                  +-------------+-----------------------+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 19]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+5.6.  Extensions State
+
+   After EAP Success, the initiator sends a token to the acceptor
+   including additional subtokens that negotiate optional features or
+   provide GSS-API channel binding (see Section 6.1).  The acceptor then
+   responds with a token to the initiator.  When the acceptor produces
+   its final token, it returns GSS_S_COMPLETE; when the initiator
+   consumes this token, it returns GSS_S_COMPLETE if no errors are
+   detected.
+
+   The acceptor SHOULD send an acceptor name response (Section 5.4.3) so
+   that the initiator can get a copy of the acceptor name protected by
+   the Message Integrity Check (MIC) subtoken.
+
+   Both the initiator and acceptor MUST include and verify a MIC
+   subtoken to protect the extensions exchange.
+
+5.6.1.  Flags Subtoken
+
+   This subtoken is sent to convey initiator flags to the acceptor.  The
+   flags are sent as a 32-bit integer in network byte order.  The only
+   flag defined so far is GSS_C_MUTUAL_FLAG, indicating that the
+   initiator successfully performed mutual authentication of the
+   acceptor.  This flag is communicated to the acceptor because some
+   protocols [RFC4462] require the acceptor to know whether the
+   initiator has confirmed its identity.  This flag has the value 0x2 to
+   be consistent with RFC 2744.
+
+                     +-------+-----------------------+
+                     | Pos   | Description           |
+                     +-------+-----------------------+
+                     | 0..3  | 0x0000000C            |
+                     |       |                       |
+                     | 4..7  | length of flags token |
+                     |       |                       |
+                     | 8..11 | flags                 |
+                     +-------+-----------------------+
+
+   Initiators MUST send 4 octets of flags.  Acceptors MUST ignore flag
+   octets beyond the first 4 and MUST ignore flag bits other than
+   GSS_C_MUTUAL_FLAG.  Initiators MUST send undefined flag bits as zero.
+
+5.6.2.  GSS Channel Bindings Subtoken
+
+   This subtoken is always critical when sent.  It is sent from the
+   initiator to the acceptor.  The contents of this token are an RFC
+   3961 get_mic token of the application data from the GSS channel
+   bindings structure passed into the context establishment call.
+
+
+
+Hartman & Howlett            Standards Track                   [Page 20]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+      +-------------+-----------------------------------------------+
+      | Pos         | Description                                   |
+      +-------------+-----------------------------------------------+
+      | 0..3        | 0x80000006                                    |
+      |             |                                               |
+      | 4..7        | length of token                               |
+      |             |                                               |
+      | 8..8+length | get_mic  of  channel binding application data |
+      +-------------+-----------------------------------------------+
+
+   Again, only the application data is sent in the channel binding.  Any
+   initiator and acceptor addresses passed by an application into
+   context establishment calls are ignored and not sent over the wire.
+   The checksum type of the get_mic token SHOULD be the mandatory-to-
+   implement checksum type of the Context Root Key (CRK).  The key to
+   use is the CRK and the key usage is 60 (KEY_USAGE_GSSEAP_CHBIND_MIC).
+   An acceptor MAY accept any MIC in the channel bindings subtoken if
+   the channel bindings input to GSS_Accept_sec_context is not provided.
+   If the channel binding input to GSS_Accept_sec_context is provided,
+   the acceptor MUST return failure if the channel binding MIC in a
+   received channel binding subtoken fails to verify.
+
+   The initiator MUST send this token if channel bindings including
+   application data are passed into GSS_Init_sec_context and MUST NOT
+   send this token otherwise.
+
+5.6.3.  MIC Subtoken
+
+   This subtoken MUST be the last subtoken in the tokens sent in
+   Extensions state.  This subtoken is sent both by the initiator and
+   acceptor.
+
+    +-------------+--------------------------------------------------+
+    | Pos         | Description                                      |
+    +-------------+--------------------------------------------------+
+    | 0..3        | 0x8000000D for initiator 0x8000000E for acceptor |
+    |             |                                                  |
+    | 4..7        | length of RFC 3961 MIC token                     |
+    |             |                                                  |
+    | 8..8+length | RFC 3961 result of get_mic                       |
+    +-------------+--------------------------------------------------+
+
+   As with any call to get_mic, a token is produced as described in RFC
+   3961 using the CRK (Section 6) as the key and the mandatory checksum
+   type for the encryption type of the CRK as the checksum type.  The
+   key usage is 61 (KEY_USAGE_GSSEAP_ACCTOKEN_MIC) for the subtoken from
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 21]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   the acceptor to the initiator and 62 (KEY_USAGE_GSSEAP_INITTOKEN_MIC)
+   for the subtoken from the initiator to the acceptor.  The input is as
+   follows:
+
+   1.  The DER-encoded object identifier of the mechanism in use; this
+       value starts with 0x06 (the tag for object identifier).  When
+       encoded in an RFC 2743 context token, the object identifier is
+       preceded by the tag and length for [Application 0] SEQUENCE.
+       This tag and the length of the overall token is not included;
+       only the tag, length, and value of the object identifier itself.
+
+   2.  A 16-bit token type in network byte order of the RFC 4121 token
+       identifier (0x0601 for initiator, 0x0602 for acceptor).
+
+   3.  For each subtoken, other than the MIC subtoken itself, the order
+       the subtokens appear in the token is as follows:
+
+   4.
+
+       1.  A four-octet subtoken type in network byte order
+
+       2.  A four-byte length in network byte order
+
+       3.  Length octets of value from that subtoken
+
+5.7.  Example Token
+
+   +----+------+----+------+-----+-------------------------+
+   | 60 |  23  | 06 |  09  | 2b  | 06 01 05 05 0f 01 01 11 |
+   +----+------+----+------+-----+-------------------------+
+   |App0|Token |OID |OID   | 1 3 |  6  1  5  5 15  1  1 17 |
+   |Tag |length|Tag |length|      Mechanism object ID      |
+   +----+------+----+------+-------------------------------+
+
+   +----------+-------------+-------------+
+   |  06 01   | 00 00 00 02 | 00 00 00 0e |
+   +----------+-------------|-------------|
+   |Initiator | Acceptor    | Length      |
+   |context   | name        | (14 octets) |
+   |token ID  | request     |             |
+   +----------+-------------+-------------+
+
+   +-------------------------------------------+
+   | 68 6f 73 74 2f 6c 6f 63 61 6c 68 6f 73 74 |
+   +-------------------------------------------+
+   | String form of acceptor name              |
+   | "host/localhost"                          |
+   +-------------------------------------------+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 22]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+                          Example Initiator Token
+
+5.8.  Context Options
+
+   GSS-API provides a number of optional per-context services requested
+   by flags on the call to GSS_Init_sec_context and indicated as outputs
+   from both GSS_Init_sec_context and GSS_Accept_sec_context.  This
+   section describes how these services are handled.  Which services the
+   client selects in the call to GSS_Init_sec_context controls what EAP
+   methods MAY be used by the client.  Section 7.2 of RFC 3748 describes
+   a set of security claims for EAP.  As described below, the selected
+   GSS options place requirements on security claims that MUST be met.
+
+   This GSS mechanism MUST only be used with EAP methods that provide
+   dictionary-attack resistance.  Typically, dictionary-attack
+   resistance is obtained by using an EAP tunnel method to tunnel an
+   inner method in TLS.
+
+   The EAP method MUST support key derivation.  Integrity,
+   confidentiality, sequencing, and replay detection MUST be indicated
+   in the output of GSS_Init_sec_context and GSS_Accept_sec_context
+   regardless of which services are requested.
+
+   The PROT_READY service defined in Section 1.2.7 of [RFC2743] is never
+   available with this mechanism.  Implementations MUST NOT offer this
+   flag or permit per-message security services to be used before
+   context establishment.
+
+   The EAP method MUST support mutual authentication and channel
+   binding.  See Section 3.4 for details on what is required for
+   successful mutual authentication.  Regardless of whether mutual
+   authentication is requested, the implementation MUST include channel
+   bindings in the EAP authentication.  If mutual authentication is
+   requested and successful mutual authentication takes place as defined
+   in Section 3.4, the initiator MUST send a flags subtoken
+   Section 5.6.1 in Extensions state.
+
+6.  Acceptor Services
+
+   The context establishment process may be passed through to an EAP
+   server via a backend authentication protocol.  However, after the EAP
+   authentication succeeds, security services are provided directly by
+   the acceptor.
+
+   This mechanism uses an RFC 3961 cryptographic key called the Context
+   Root Key (CRK).  The CRK is derived from the GMSK (GSS-API Master
+   Session Key).  The GMSK is the result of the random-to-key [RFC3961]
+   operation of the encryption type of this mechanism consuming the
+
+
+
+Hartman & Howlett            Standards Track                   [Page 23]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   appropriate number of bits from the EAP MSK.  For example, for
+   aes128-cts-hmac-sha1-96, the random-to-key operation consumes 16
+   octets of key material; thus, the first 16 bytes of the MSK are input
+   to random-to-key to form the GMSK.  If the MSK is too short,
+   authentication MUST fail.
+
+   In the following, pseudorandom is the RFC 3961 pseudorandom operation
+   for the encryption type of the GMSK and random-to-key is the RFC 3961
+   random-to-key operation for the enctype of the mechanism.  The
+   truncate function takes the initial l bits of its input.  The goal in
+   constructing a CRK is to call the pseudorandom function enough times
+   to produce the right number of bits of output and discard any excess
+   bits of output.
+
+   The CRK is derived from the GMSK using the following procedure:
+
+   Tn = pseudorandom(GMSK, n || "rfc4121-gss-eap")
+   CRK = random-to-key(truncate(L, T0 || T1 || .. || Tn))
+   L = random-to-key input size
+
+   Where n is a 32-bit integer in network byte order starting at 0 and
+   incremented to each call to the pseudo_random operation.
+
+6.1.  GSS-API Channel Binding
+
+   GSS-API channel binding [RFC5554] is a protected facility for
+   exchanging a cryptographic name for an enclosing channel between the
+   initiator and acceptor.  The initiator sends channel binding data and
+   the acceptor confirms that channel binding data has been checked.
+
+   The acceptor SHOULD accept any channel binding provided by the
+   initiator if null channel bindings are passed into
+   gss_accept_sec_context.  Protocols such as HTTP Negotiate [RFC4559]
+   depend on this behavior of some Kerberos implementations.
+
+   As discussed, the GSS channel bindings subtoken is sent in the
+   Extensions state.
+
+6.2.  Per-Message Security
+
+   The per-message tokens of Section 4 of RFC 4121 are used.  The CRK
+   SHALL be treated as the initiator sub-session key, the acceptor sub-
+   session key and the ticket session key.
+
+6.3.  Pseudorandom Function
+
+   The pseudorandom function defined in [RFC4402] is used to provide
+   GSS_Pseudo_Random functionality to applications.
+
+
+
+Hartman & Howlett            Standards Track                   [Page 24]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+7.  IANA Considerations
+
+   This specification creates a number of IANA registries.
+
+7.1.  OID Registry
+
+   IANA has created a registry of ABFAB object identifiers titled
+   "Object Identifiers for Application Bridging for Federated Access".
+   The initial contents of the registry are specified below.  The
+   registration policy is IETF Review or IESG Approval [RFC5226].  Early
+   allocation is permitted.  IANA has updated the reference for the root
+   of this OID delegation to point to the newly created registry.
+
+   Decimal   Name        Description                         References
+   -------   ----        ----------------------------------  ----------
+         0   Reserved    Reserved                            RFC 7055
+         1   mechanisms  A sub-arc containing ABFAB          RFC 7055
+                         mechanisms
+         2   nametypes   A sub-arc containing ABFAB          RFC 7055
+                         GSS-API Name Types
+
+   Prefix:
+   iso.org.dod.internet.security.mechanisms.abfab
+           (1.3.6.1.5.5.15)
+
+   NOTE: the following mechanisms registry is the root of the OID for
+   the mechanism in question.  As discussed in Section 5.1, a Kerberos
+   encryption type number [RFC3961] is appended to the mechanism version
+   OID below to form the OID of a specific mechanism.
+
+   Prefix:
+   iso.org.dod.internet.security.mechanisms.abfab.mechanisms
+           (1.3.6.1.5.5.15.1)
+
+   Decimal   Name          Description                      References
+   -------   ----          -------------------------------  ----------
+         0   Reserved      Reserved                         RFC 7055
+         1   gss-eap-v1    The GSS-EAP mechanism            RFC 7055
+
+   Prefix:
+   iso.org.dod.internet.security.mechanisms.abfab.nametypes
+           (1.3.6.1.5.5.15.2)
+
+   Decimal   Name          Description            References
+   -------   ----          ---------------------  ----------
+         0   Reserved      Reserved               RFC 7055
+         1   GSS_EAP_NT_EAP_NAME                  RFC 7055, Section 3.1
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 25]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+7.2.  RFC 4121 Token Identifiers
+
+   In the top-level registry titled "Kerberos V GSS-API Mechanism
+   Parameters", a subregistry called "Kerberos GSS-API Token Type
+   Identifiers" was created; the references for this subregistry are RFC
+   4121 and this document.  The allocation procedure is Expert Review
+   [RFC5226].  The Expert's primary job is to make sure that token type
+   identifiers are requested by an appropriate requester for the RFC
+   4121 mechanism in which they will be used and that multiple values
+   are not allocated for the same purpose.  For RFC 4121 and this
+   mechanism, the Expert is currently expected to make allocations for
+   token identifiers from documents in the IETF stream; effectively, for
+   these mechanisms, the Expert currently confirms the allocation meets
+   the requirements of the IETF Review process.
+
+   The ID field is a hexadecimal token identifier specified in network
+   byte order.
+
+   The initial registrations are as follows:
+
+   +-------+-------------------------------+---------------------------+
+   | ID    | Description                   | Reference                 |
+   +-------+-------------------------------+---------------------------+
+   | 01 00 | KRB_AP_REQ                    | RFC 4121, Section 4.1     |
+   |       |                               |                           |
+   | 02 00 | KRB_AP_REP                    | RFC 4121, Section 4.1     |
+   |       |                               |                           |
+   | 03 00 | KRB_ERROR                     | RFC 4121, Section 4.1     |
+   |       |                               |                           |
+   | 04 04 | MIC tokens                    | RFC 4121, Section 4.2.6.1 |
+   |       |                               |                           |
+   | 05 04 | wrap tokens                   | RFC 4121, Section 4.2.6.2 |
+   |       |                               |                           |
+   | 06 01 | GSS-EAP initiator context     | RFC 7055, Section 5       |
+   |       | token                         |                           |
+   |       |                               |                           |
+   | 06 02 | GSS EAP acceptor context      | RFC 7055, Section 5       |
+   |       | token                         |                           |
+   +-------+-------------------------------+---------------------------+
+
+7.3.  GSS-EAP Subtoken Types
+
+   This document creates a top-level registry called "The Extensible
+   Authentication Protocol Mechanism for the Generic Security Service
+   Application Programming Interface (GSS-EAP) Parameters".  In any
+   short form of that name, including any URI for this registry, it is
+   important that the string GSS come before the string EAP; this will
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 26]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   help to distinguish registries if EAP methods for performing GSS-API
+   authentication are ever defined.
+
+   In this registry is a subregistry of subtoken types.  Identifiers are
+   32-bit integers; the upper bit (0x80000000) is reserved as a critical
+   flag and should not be indicated in the registration.  Assignments of
+   GSS-EAP subtoken types are made by Expert Review [RFC5226].  The
+   Expert is expected to require a public specification of the subtoken
+   similar in detail to registrations given in this document.  The
+   security of GSS-EAP depends on making sure that subtoken information
+   has adequate protection and that the overall mechanism continues to
+   be secure.  Examining the security and architectural consistency of
+   the proposed registration is the primary responsibility of the
+   Expert.
+
+         +------------+--------------------------+---------------+
+         | Type       | Description              | Reference     |
+         +------------+--------------------------+---------------+
+         | 0x00000001 | Error                    | Section 5.3   |
+         |            |                          |               |
+         | 0x0000000B | Vendor                   | Section 5.4.1 |
+         |            |                          |               |
+         | 0x00000002 | Acceptor name request    | Section 5.4.2 |
+         |            |                          |               |
+         | 0x00000003 | Acceptor name response   | Section 5.4.3 |
+         |            |                          |               |
+         | 0x00000005 | EAP request              | Section 5.5.1 |
+         |            |                          |               |
+         | 0x00000004 | EAP response             | Section 5.5.2 |
+         |            |                          |               |
+         | 0x0000000C | Flags                    | Section 5.6.1 |
+         |            |                          |               |
+         | 0x00000006 | GSS-API channel bindings | Section 5.6.2 |
+         |            |                          |               |
+         | 0x0000000D | Initiator MIC            | Section 5.6.3 |
+         |            |                          |               |
+         | 0x0000000E | Acceptor MIC             | Section 5.6.3 |
+         +------------+--------------------------+---------------+
+
+7.4.  RADIUS Attribute Assignments
+
+   The following RADIUS attribute type values [RFC3575] are assigned.
+   The allocation instructions in Section 10.3 of [RFC6929] have been
+   followed.
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 27]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   +--------------------------------+-------+--------------------------+
+   | Description                    | Value | More Information         |
+   +--------------------------------+-------+--------------------------+
+   | GSS-Acceptor-Service-Name      | 164   | user-or-service portion  |
+   |                                |       | of name                  |
+   |                                |       |                          |
+   | GSS-Acceptor-Host-Name         | 165   | host portion of name     |
+   |                                |       |                          |
+   | GSS-Acceptor-Service-Specifics | 166   | service-specifics        |
+   |                                |       | portion of name          |
+   |                                |       |                          |
+   | GSS-Acceptor-Realm-Name        | 167   | Realm portion of name    |
+   +--------------------------------+-------+--------------------------+
+
+7.5.  Registration of the EAP-AES128 SASL Mechanisms
+
+   Subject:  Registration of SASL mechanisms EAP-AES128 and
+      EAP-AES128-PLUS
+
+   SASL mechanism names:  EAP-AES128 and EAP-AES128-PLUS
+
+   Security considerations:  See RFC 5801 and RFC 7055
+
+   Published specification (recommended):  RFC 7055
+
+   Person & email address to contact for further information:
+      Abfab Working Group, abfab@ietf.org
+
+   Intended usage:  common
+
+   Owner/Change controller:  iesg@ietf.org
+
+   Note:  This mechanism describes the GSS-EAP mechanism used with the
+      aes128-cts-hmac-sha1-96 enctype.  The GSS-API OID for this
+      mechanism is 1.3.6.1.5.5.15.1.1.17.
+
+      As described in RFC 5801, a PLUS variant of this mechanism is also
+      required.
+
+7.6.  GSS-EAP Errors
+
+   A new subregistry is created in the GSS-EAP parameters registry
+   titled "GSS-EAP Error Codes".  The error codes in this registry are
+   unsigned 32-bit numbers.  Values less than or equal to 127 are
+   assigned by Standards Action [RFC5226].  Values 128 through 255 are
+   assigned with the Specification Required assignment policy [RFC5226].
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 28]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   Values greater than 255 are reserved; updates to registration policy
+   may make these values available for assignment and implementations
+   MUST be prepared to receive them.
+
+   This table provides the initial contents of the registry.
+
+        +-------+------------------------------------------------+
+        | Value | Description                                    |
+        +-------+------------------------------------------------+
+        | 0     | Reserved                                       |
+        |       |                                                |
+        | 1     | Buffer is incorrect size                       |
+        |       |                                                |
+        | 2     | Incorrect mechanism OID                        |
+        |       |                                                |
+        | 3     | Token is corrupted                             |
+        |       |                                                |
+        | 4     | Token is truncated                             |
+        |       |                                                |
+        | 5     | Packet received by direction that sent it      |
+        |       |                                                |
+        | 6     | Incorrect token type identifier                |
+        |       |                                                |
+        | 7     | Unhandled critical subtoken received           |
+        |       |                                                |
+        | 8     | Missing required subtoken                      |
+        |       |                                                |
+        | 9     | Duplicate subtoken type                        |
+        |       |                                                |
+        | 10    | Received unexpected subtoken for current state |
+        |       |                                                |
+        | 11    | EAP did not produce a key                      |
+        |       |                                                |
+        | 12    | EAP key too short                              |
+        |       |                                                |
+        | 13    | Authentication rejected                        |
+        |       |                                                |
+        | 14    | AAA returned an unexpected message type        |
+        |       |                                                |
+        | 15    | AAA response did not include EAP request       |
+        |       |                                                |
+        | 16    | Generic AAA failure                            |
+        +-------+------------------------------------------------+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 29]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+7.7.  GSS-EAP Context Flags
+
+   A new subregistry is created in the GSS-EAP parameters registry.
+   This registry holds registrations of flag bits sent in the flags
+   subtoken (Section 5.6.1).  There are 32 flag bits available for
+   registration represented as hexadecimal numbers from the most
+   significant bit 0x80000000 to the least significant bit 0x1.  The
+   registration policy for this registry is IETF Review or, in
+   exceptional cases, IESG Approval.  The following table indicates
+   initial registrations; all other values are available for assignment.
+
+               +------+-------------------+---------------+
+               | Flag | Name              | Reference     |
+               +------+-------------------+---------------+
+               | 0x2  | GSS_C_MUTUAL_FLAG | Section 5.6.1 |
+               +------+-------------------+---------------+
+
+8.  Security Considerations
+
+   RFC 3748 discusses security issues surrounding EAP.  RFC 5247
+   discusses the security and requirements surrounding key management
+   that leverages the AAA infrastructure.  These documents are critical
+   to the security analysis of this mechanism.
+
+   RFC 2743 discusses generic security considerations for the GSS-API.
+   RFC 4121 discusses security issues surrounding the specific per-
+   message services used in this mechanism.
+
+   As discussed in Section 4, this mechanism may introduce multiple
+   layers of security negotiation into application protocols.  Multiple
+   layer negotiations are vulnerable to a bid-down attack when a
+   mechanism negotiated at the outer layer is preferred to some but not
+   all mechanisms negotiated at the inner layer; see Section 7.3 of
+   [RFC4462] for an example.  One possible approach to mitigate this
+   attack is to construct security policy such that the preference for
+   all mechanisms negotiated in the inner layer falls between
+   preferences for two outer-layer mechanisms or falls at one end of the
+   overall ranked preferences including both the inner and outer layer.
+   Another approach is to only use this mechanism when it has
+   specifically been selected for a given service.  The second approach
+   is likely to be common in practice because one common deployment will
+   involve an EAP supplicant interacting with a user to select a given
+   identity.  Only when an identity is successfully chosen by the user
+   will this mechanism be attempted.
+
+   EAP channel binding is used to give the GSS-API initiator confidence
+   in the identity of the GSS-API acceptor.  Thus, the security of this
+   mechanism depends on the use and verification of EAP channel binding.
+
+
+
+Hartman & Howlett            Standards Track                   [Page 30]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   Today, EAP channel binding is in very limited deployment.  If EAP
+   channel binding is not used, then the system may be vulnerable to
+   phishing attacks where a user is diverted from one service to
+   another.  If the EAP method in question supports mutual
+   authentication then users can only be diverted between servers that
+   are part of the same AAA infrastructure.  For deployments where
+   membership in the AAA infrastructure is limited, this may serve as a
+   significant limitation on the value of phishing as an attack.  For
+   other deployments, use of EAP channel binding is critical to avoid
+   phishing.  These attacks are possible with EAP today although not
+   typically with common GSS-API mechanisms.  For this reason,
+   implementations are required to implement and use EAP channel
+   binding; see Section 3 for details.
+
+   The security considerations of EAP channel binding [RFC6677] describe
+   the security properties of channel binding.  Two attacks are worth
+   calling out here.  First, when a tunneled EAP method is used, it is
+   critical that the channel binding be performed with an EAP server
+   trusted by the peer.  With existing EAP methods, this typically
+   requires validating the certificate of the server tunnel endpoint
+   back to a trust anchor and confirming the name of the entity who is a
+   subject of that certificate.  EAP methods may suffer from bid-down
+   attacks where an attacker can cause a peer to think that a particular
+   EAP server does not support channel binding.  This does not directly
+   cause a problem because mutual authentication is only offered at the
+   GSS-API level when channel binding to the server's identity is
+   successful.  However, when an EAP method is not vulnerable to these
+   bid-down attacks, additional protection is available.  This mechanism
+   will benefit significantly from new strong EAP methods such as
+   [TEAP].
+
+   Every proxy in the AAA chain from the authenticator to the EAP server
+   needs to be trusted to help verify channel bindings and to protect
+   the integrity of key material.  GSS-API applications may be built to
+   assume a trust model where the acceptor is directly responsible for
+   authentication.  However, GSS-API is definitely used with trusted-
+   third-party mechanisms such as Kerberos.
+
+   RADIUS does provide a weak form of hop-by-hop confidentiality of key
+   material based on using MD5 as a stream cipher.  Diameter can use TLS
+   or IPsec but has no mandatory-to-implement confidentiality mechanism.
+   Operationally, protecting key material as it is transported between
+   the Identity Provider (IdP) and Relying Party (RP) is critical to
+   per-message security and verification of GSS-API channel binding
+   [RFC5056].  Mechanisms such as RADIUS over TLS [RFC6614] provide
+   significantly better protection of key material than the base RADIUS
+   specification.
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 31]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+9.  Acknowledgements
+
+   Luke Howard, Jim Schaad, Alejandro Perez Mendez, Alexey Melnikov, and
+   Sujing Zhou provided valuable reviews of this document.
+
+   Rhys Smith provided the text for the OID registry section.  Sam
+   Hartman's work on this document has been funded by JANET.
+
+10.  References
+
+10.1.  Normative References
+
+   [GSS-IANA] IANA, "GSS-API Service Name Registry",
+              <http://www.iana.org/assignments/gssapi-service-names>.
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [RFC2743]  Linn, J., "Generic Security Service Application Program
+              Interface Version 2, Update 1", RFC 2743, January 2000.
+
+   [RFC2744]  Wray, J., "Generic Security Service API Version 2 :
+              C-bindings", RFC 2744, January 2000.
+
+   [RFC3575]  Aboba, B., "IANA Considerations for RADIUS (Remote
+              Authentication Dial In User Service)", RFC 3575, July
+              2003.
+
+   [RFC3748]  Aboba, B., Blunk, L., Vollbrecht, J., Carlson, J., and H.
+              Levkowetz, "Extensible Authentication Protocol (EAP)", RFC
+              3748, June 2004.
+
+   [RFC3961]  Raeburn, K., "Encryption and Checksum Specifications for
+              Kerberos 5", RFC 3961, February 2005.
+
+   [RFC4121]  Zhu, L., Jaganathan, K., and S. Hartman, "The Kerberos
+              Version 5 Generic Security Service Application Program
+              Interface (GSS-API) Mechanism: Version 2", RFC 4121, July
+              2005.
+
+   [RFC4282]  Aboba, B., Beadles, M., Arkko, J., and P. Eronen, "The
+              Network Access Identifier", RFC 4282, December 2005.
+
+   [RFC4401]  Williams, N., "A Pseudo-Random Function (PRF) API
+              Extension for the Generic Security Service Application
+              Program Interface (GSS-API)", RFC 4401, February 2006.
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 32]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   [RFC4402]  Williams, N., "A Pseudo-Random Function (PRF) for the
+              Kerberos V Generic Security Service Application Program
+              Interface (GSS-API) Mechanism", RFC 4402, February 2006.
+
+   [RFC5056]  Williams, N., "On the Use of Channel Bindings to Secure
+              Channels", RFC 5056, November 2007.
+
+   [RFC5226]  Narten, T. and H. Alvestrand, "Guidelines for Writing an
+              IANA Considerations Section in RFCs", BCP 26, RFC 5226,
+              May 2008.
+
+   [RFC5234]  Crocker, D. and P. Overell, "Augmented BNF for Syntax
+              Specifications: ABNF", STD 68, RFC 5234, January 2008.
+
+   [RFC5554]  Williams, N., "Clarifications and Extensions to the
+              Generic Security Service Application Program Interface
+              (GSS-API) for the Use of Channel Bindings", RFC 5554, May
+              2009.
+
+   [RFC5891]  Klensin, J., "Internationalized Domain Names in
+              Applications (IDNA): Protocol", RFC 5891, August 2010.
+
+   [RFC6677]  Hartman, S., Clancy, T., and K. Hoeper, "Channel-Binding
+              Support for Extensible Authentication Protocol (EAP)
+              Methods", RFC 6677, July 2012.
+
+   [RFC7057]  Winter, S. and J. Salowey, "Update to the Extensible
+              Authentication Protocol (EAP) Applicability Statement for
+              Application Bridging for Federated Access Beyond Web
+              (ABFAB)", RFC 7057, December 2013.
+
+10.2.  Informative References
+
+   [ABFAB-ARCH]
+              Howlett, J., Hartman, S., Tschofenig, H., Lear, E., and J.
+              Schaad, "Application Bridging for Federated Access Beyond
+              Web (ABFAB) Architecture", Work in Progress, July 2013.
+
+   [RFC1964]  Linn, J., "The Kerberos Version 5 GSS-API Mechanism", RFC
+              1964, June 1996.
+
+   [RFC3579]  Aboba, B. and P. Calhoun, "RADIUS (Remote Authentication
+              Dial In User Service) Support For Extensible
+              Authentication Protocol (EAP)", RFC 3579, September 2003.
+
+   [RFC4072]  Eronen, P., Hiller, T., and G. Zorn, "Diameter Extensible
+              Authentication Protocol (EAP) Application", RFC 4072,
+              August 2005.
+
+
+
+Hartman & Howlett            Standards Track                   [Page 33]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+   [RFC4178]  Zhu, L., Leach, P., Jaganathan, K., and W. Ingersoll, "The
+              Simple and Protected Generic Security Service Application
+              Program Interface (GSS-API) Negotiation Mechanism", RFC
+              4178, October 2005.
+
+   [RFC4422]  Melnikov, A. and K. Zeilenga, "Simple Authentication and
+              Security Layer (SASL)", RFC 4422, June 2006.
+
+   [RFC4462]  Hutzelman, J., Salowey, J., Galbraith, J., and V. Welch,
+              "Generic Security Service Application Program Interface
+              (GSS-API) Authentication and Key Exchange for the Secure
+              Shell (SSH) Protocol", RFC 4462, May 2006.
+
+   [RFC4559]  Jaganathan, K., Zhu, L., and J. Brezak, "SPNEGO-based
+              Kerberos and NTLM HTTP Authentication in Microsoft
+              Windows", RFC 4559, June 2006.
+
+   [RFC5178]  Williams, N. and A. Melnikov, "Generic Security Service
+              Application Program Interface (GSS-API)
+              Internationalization and Domain-Based Service Names and
+              Name Type", RFC 5178, May 2008.
+
+   [RFC5247]  Aboba, B., Simon, D., and P. Eronen, "Extensible
+              Authentication Protocol (EAP) Key Management Framework",
+              RFC 5247, August 2008.
+
+   [RFC6066]  Eastlake, D., "Transport Layer Security (TLS) Extensions:
+              Extension Definitions", RFC 6066, January 2011.
+
+   [RFC6614]  Winter, S., McCauley, M., Venaas, S., and K. Wierenga,
+              "Transport Layer Security (TLS) Encryption for RADIUS",
+              RFC 6614, May 2012.
+
+   [RFC6929]  DeKok, A. and A. Lior, "Remote Authentication Dial In User
+              Service (RADIUS) Protocol Extensions", RFC 6929, April
+              2013.
+
+   [TEAP]     Zhou, H., Cam-Winget, N., Salowey, J., and S. Hanna,
+              "Tunnel EAP Method (TEAP) Version 1", Work in Progress,
+              September 2013.
+
+
+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 34]
+\f
+RFC 7055                       EAP GSS-API                 December 2013
+
+
+Appendix A.  Pre-publication RADIUS VSA
+
+   As described in Section 3.4, RADIUS attributes are used to carry the
+   acceptor name when this family of mechanisms is used with RADIUS.
+   Prior to the publication of this specification, a vendor-specific
+   RADIUS attribute was used.  This non-normative appendix documents
+   that attribute as it may be seen from older implementations.
+
+   Prior to IANA assignment, GSS-EAP used a RADIUS vendor-specific
+   attribute for carrying the acceptor name.  The Vendor-Specific
+   Attribute (VSA) with enterprise ID 25622 is formatted as a VSA
+   according to the recommendation in the RADIUS specification.  The
+   following sub-attributes are defined:
+
+   +--------------------------------+-----------+----------------------+
+   | Name                           | Attribute | Description          |
+   +--------------------------------+-----------+----------------------+
+   | GSS-Acceptor-Service-Name      | 128       | user-or-service      |
+   |                                |           | portion of name      |
+   |                                |           |                      |
+   | GSS-Acceptor-Host-Name         | 129       | host portion of name |
+   |                                |           |                      |
+   | GSS-Acceptor-Service-Specifics | 130       | service-specifics    |
+   |                                |           | portion of name      |
+   |                                |           |                      |
+   | GSS-Acceptor-Realm-Name        | 131       | Realm portion of     |
+   |                                |           | name                 |
+   +--------------------------------+-----------+----------------------+
+
+Authors' Addresses
+
+   Sam Hartman (editor)
+   Painless Security
+
+   EMail: hartmans-ietf@mit.edu
+
+
+   Josh Howlett
+   JANET(UK)
+
+   EMail: josh.howlett@ja.net
+
+
+
+
+
+
+
+
+
+
+Hartman & Howlett            Standards Track                   [Page 35]
+\f
diff --git a/doc/rfc/rfc7268.txt b/doc/rfc/rfc7268.txt
new file mode 100644 (file)
index 0000000..e33913d
--- /dev/null
@@ -0,0 +1,1627 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF)                          B. Aboba
+Request for Comments: 7268                         Microsoft Corporation
+Updates: 3580, 4072                                           J. Malinen
+Category: Standards Track                                    Independent
+ISSN: 2070-1721                                               P. Congdon
+                                                         Tallac Networks
+                                                              J. Salowey
+                                                           Cisco Systems
+                                                                M. Jones
+                                                           Azuca Systems
+                                                               July 2014
+
+
+                RADIUS Attributes for IEEE 802 Networks
+
+Abstract
+
+   RFC 3580 provides guidelines for the use of the Remote Authentication
+   Dial-In User Service (RADIUS) within IEEE 802 local area networks
+   (LANs).  This document defines additional attributes for use within
+   IEEE 802 networks and clarifies the usage of the EAP-Key-Name
+   Attribute and the Called-Station-Id Attribute.  This document updates
+   RFCs 3580 and 4072.
+
+Status of This Memo
+
+   This is an Internet Standards Track document.
+
+   This document is a product of the Internet Engineering Task Force
+   (IETF).  It represents the consensus of the IETF community.  It has
+   received public review and has been approved for publication by the
+   Internet Engineering Steering Group (IESG).  Further information on
+   Internet Standards is available in Section 2 of RFC 5741.
+
+   Information about the current status of this document, any errata,
+   and how to provide feedback on it may be obtained at
+   http://www.rfc-editor.org/info/rfc7268.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                    [Page 1]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+Copyright Notice
+
+   Copyright (c) 2014 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+   This document may contain material from IETF Documents or IETF
+   Contributions published or made publicly available before November
+   10, 2008.  The person(s) controlling the copyright in some of this
+   material may not have granted the IETF Trust the right to allow
+   modifications of such material outside the IETF Standards Process.
+   Without obtaining an adequate license from the person(s) controlling
+   the copyright in such materials, this document may not be modified
+   outside the IETF Standards Process, and derivative works of it may
+   not be created outside the IETF Standards Process, except to format
+   it for publication as an RFC or to translate it into languages other
+   than English.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                    [Page 2]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+Table of Contents
+
+   1. Introduction ....................................................3
+      1.1. Terminology ................................................4
+      1.2. Requirements Language ......................................4
+   2. RADIUS Attributes ...............................................5
+      2.1. Allowed-Called-Station-Id ..................................5
+      2.2. EAP-Key-Name ...............................................6
+      2.3. EAP-Peer-Id ................................................7
+      2.4. EAP-Server-Id ..............................................8
+      2.5. Mobility-Domain-Id .........................................9
+      2.6. Preauth-Timeout ...........................................10
+      2.7. Network-Id-Name ...........................................11
+      2.8. EAPoL-Announcement ........................................12
+      2.9. WLAN-HESSID ...............................................14
+      2.10. WLAN-Venue-Info ..........................................14
+      2.11. WLAN-Venue-Language ......................................16
+      2.12. WLAN-Venue-Name ..........................................17
+      2.13. WLAN-Reason-Code .........................................18
+      2.14. WLAN-Pairwise-Cipher .....................................19
+      2.15. WLAN-Group-Cipher ........................................20
+      2.16. WLAN-AKM-Suite ...........................................21
+      2.17. WLAN-Group-Mgmt-Cipher ...................................22
+      2.18. WLAN-RF-Band .............................................23
+   3. Table of Attributes ............................................24
+   4. IANA Considerations ............................................25
+   5. Security Considerations ........................................25
+   6. References .....................................................26
+      6.1. Normative References ......................................26
+      6.2. Informative References ....................................27
+   7. Acknowledgments ................................................28
+
+1.  Introduction
+
+   In situations where it is desirable to centrally manage
+   authentication, authorization, and accounting (AAA) for IEEE 802
+   [IEEE-802] networks, deployment of a backend authentication and
+   accounting server is desirable.  In such situations, it is expected
+   that IEEE 802 authenticators will function as AAA clients.
+
+   "IEEE 802.1X Remote Authentication Dial In User Service (RADIUS)
+   Usage Guidelines" [RFC3580] provides guidelines for the use of the
+   Remote Authentication Dial-In User Service (RADIUS) within networks
+   utilizing IEEE 802 local area networks.  This document defines
+   additional attributes suitable for usage by IEEE 802 authenticators
+   acting as AAA clients.
+
+
+
+
+
+Aboba, et al.                Standards Track                    [Page 3]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+1.1.  Terminology
+
+   This document uses the following terms:
+
+   Access Point (AP)
+      A Station that provides access to the distribution services via
+      the wireless medium for associated Stations.
+
+   Association
+      The service used to establish Access Point/Station mapping and
+      enable Station invocation of the distribution system services.
+
+   Authenticator
+      An entity that requires authentication from the Supplicant.  The
+      authenticator may be connected to the Supplicant at the other end
+      of a point-to-point LAN segment or wireless link.
+
+   Authentication Server
+      An entity that provides an authentication service to an
+      authenticator.  This service verifies the claim of identity made
+      by the Supplicant using the credentials provided by the Supplicant
+
+   Station (STA)
+      Any device that contains an IEEE 802.11 conformant Medium Access
+      Control (MAC) and Physical Layer (PHY) interface to the wireless
+      medium (WM).
+
+   Supplicant
+      An entity that is being authenticated by an authenticator.  The
+      Supplicant may be connected to the authenticator at one end of a
+      point-to-point LAN segment or 802.11 wireless link.
+
+1.2.  Requirements Language
+
+   In this document, several words are used to signify the requirements
+   of the specification.  The key words "MUST", "MUST NOT", "REQUIRED",
+   "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED",  "MAY",
+   and "OPTIONAL" in this document are to be interpreted as described in
+   [RFC2119].
+
+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                    [Page 4]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.  RADIUS Attributes
+
+2.1.  Allowed-Called-Station-Id
+
+   Description
+
+      The Allowed-Called-Station-Id Attribute allows the RADIUS server
+      to specify the authenticator MAC addresses and/or networks to
+      which the user is allowed to connect.  One or more Allowed-Called-
+      Station-Id Attributes MAY be included in an Access-Accept, CoA-
+      Request, or Accounting-Request packet.
+
+      The Allowed-Called-Station-Id Attribute can be useful in
+      situations where pre-authentication is supported (e.g., IEEE
+      802.11 pre-authentication).  In these scenarios, a Called-Station-
+      Id Attribute typically will not be included within the Access-
+      Request so that the RADIUS server will not know the network that
+      the user is attempting to access.  The Allowed-Called-Station-Id
+      enables the RADIUS server to restrict the networks and attachment
+      points to which the user can subsequently connect.
+
+      A summary of the Allowed-Called-Station-Id Attribute format is
+      shown below.  The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |            String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      174
+
+   Length
+
+      >=3
+
+   String
+
+      The String field is one or more octets, specifying a Called-
+      Station-Id that the user MAY connect to; if the Called-Station-Id
+      that the user connects to does not match one of the Allowed-
+      Called-Station-Id Attributes, the Network Access Server (NAS) MUST
+      NOT permit the user to access the network.
+
+
+
+
+
+
+Aboba, et al.                Standards Track                    [Page 5]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      In the case of IEEE 802, the Allowed-Called-Station-Id Attribute
+      is used to store the Medium Access Control (MAC) address,
+      represented as an uppercase ASCII character string in Canonical
+      format and with octet values separated by a "-", for example,
+      "00-10-A4-23-19-C0".  Where restrictions on both the network and
+      authenticator MAC address usage are intended, the network name
+      MUST be appended to the authenticator MAC address, separated from
+      the MAC address with a ":", for example, "00-10-A4-23-19-C0:AP1".
+      Where no MAC address restriction is intended, the MAC address
+      field MUST be omitted, but ":" and the network name field MUST be
+      included, for example, ":AP1".
+
+      Within IEEE 802.11 [IEEE-802.11], the Service Set Identifier
+      (SSID) constitutes the network name; within IEEE 802.1X
+      [IEEE-802.1X] wired networks, the Network-Id Name (NID-Name)
+      constitutes the network name.  Since a NID-Name can be up to 253
+      octets in length, when used with [IEEE-802.1X] wired networks,
+      there may not be sufficient room within the Allowed-Called-
+      Station-Id Attribute to include both a MAC address and a network
+      name.  However, as the Allowed-Called-Station-Id Attribute is
+      expected to be used largely in wireless access scenarios, this
+      restriction is not considered serious.
+
+2.2.  EAP-Key-Name
+
+   Description
+
+      The EAP-Key-Name Attribute, defined in "Diameter Extensible
+      Authentication Protocol (EAP) Application" [RFC4072], contains the
+      EAP Session-Id, as described in "Extensible Authentication
+      Protocol (EAP) Key Management Framework" [RFC5247].  Exactly how
+      this attribute is used depends on the link layer in question.
+
+      It should be noted that not all link layers use this name.  An
+      EAP-Key-Name Attribute MAY be included within Access-Request,
+      Access-Accept, and CoA-Request packets.  A summary of the EAP-Key-
+      Name Attribute format is shown below.  The fields are transmitted
+      from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |          String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      102 [RFC4072]
+
+
+
+Aboba, et al.                Standards Track                    [Page 6]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+   Length
+
+      >=3
+
+   String
+
+      The String field is one or more octets, containing the EAP
+      Session-Id, as defined in "Extensible Authentication Protocol
+      (EAP) Key Management Framework" [RFC5247].  Since the NAS operates
+      as a pass-through in EAP, it cannot know the EAP Session-Id before
+      receiving it from the RADIUS server.  As a result, an EAP-Key-Name
+      Attribute sent in an Access-Request MUST only contain a single NUL
+      character.  A RADIUS server receiving an Access-Request with an
+      EAP-Key-Name Attribute containing anything other than a single NUL
+      character MUST silently discard the attribute.  In addition, the
+      RADIUS server SHOULD include this attribute in an Access-Accept or
+      CoA-Request only if an EAP-Key-Name Attribute was present in the
+      Access-Request.  Since a NAS will typically only include an EAP-
+      Key-Name Attribute in an Access-Request in situations where the
+      attribute is required to provision service, if an EAP-Key-Name
+      Attribute is included in an Access-Request but is not present in
+      the Access-Accept, the NAS SHOULD treat the Access-Accept as
+      though it were an Access-Reject.  If an EAP-Key-Name Attribute was
+      not present in the Access-Request but is included in the Access-
+      Accept, then the NAS SHOULD silently discard the EAP-Key-Name
+      Attribute.  As noted in Section 6.2.2 of [IEEE-802.1X], the
+      Connectivity Association Key Name (CKN) is derived from the EAP
+      Session-Id, and, as described in Section 9.3.3 of [IEEE-802.1X],
+      the CKN is subsequently used in the derivation of the Key
+      Encrypting Key (KEK) and the Integrity Check Value Key (ICK),
+      which protect the Secure Association Keys (SAKs) utilized by Media
+      Access Control Security (MACsec).  As a result, for the NAS to
+      acquire information needed in the MACsec Key Agreement (MKA)
+      exchange, it needs to include the EAP-Key-Name Attribute in the
+      Access-Request and receive it from the RADIUS server in the
+      Access-Accept.
+
+2.3.  EAP-Peer-Id
+
+   Description
+
+      The EAP-Peer-Id Attribute contains a Peer-Id generated by the EAP
+      method.  Exactly how this name is used depends on the link layer
+      in question.  See [RFC5247] for more discussion.  The EAP-Peer-Id
+      Attribute MAY be included in Access-Request, Access-Accept, and
+      Accounting-Request packets.  More than one EAP-Peer-Id Attribute
+      MUST NOT be included in an Access-Request; one or more EAP-Peer-Id
+      Attributes MAY be included in an Access-Accept.
+
+
+
+Aboba, et al.                Standards Track                    [Page 7]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      It should be noted that not all link layers use this name, and
+      existing EAP method implementations do not generate it.  Since the
+      NAS operates as a pass-through in EAP [RFC3748], it cannot know
+      the EAP-Peer-Id before receiving it from the RADIUS server.  As a
+      result, an EAP-Peer-Id Attribute sent in an Access-Request MUST
+      only contain a single NUL character.  A home RADIUS server
+      receiving an Access-Request with an EAP-Peer-Id Attribute
+      containing anything other than a single NUL character MUST
+      silently discard the attribute.  In addition, the home RADIUS
+      server SHOULD include one or more EAP-Peer-Id Attributes in an
+      Access-Accept only if an EAP-Peer-Id Attribute was present in the
+      Access-Request.  If a NAS receives EAP-Peer-Id Attribute(s) in an
+      Access-Accept without having included one in an Access-Request,
+      the NAS SHOULD silently discard the attribute(s).  A summary of
+      the EAP-Peer-Id Attribute format is shown below.  The fields are
+      transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |            String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      175
+
+   Length
+
+      >=3
+
+   String
+
+      The String field is one or more octets, containing an EAP Peer-Id
+      exported by the EAP method.  For details, see Appendix A of
+      [RFC5247].  A robust implementation SHOULD support the field as
+      undistinguished octets.  Only a single EAP Peer-Id may be included
+      per attribute.
+
+2.4.  EAP-Server-Id
+
+   Description
+
+      The EAP-Server-Id Attribute contains a Server-Id generated by the
+      EAP method.  Exactly how this name is used depends on the link
+      layer in question.  See [RFC5247] for more discussion.  The EAP-
+      Server-Id Attribute is only allowed in Access-Request, Access-
+      Accept, and Accounting-Request packets.  More than one EAP-Server-
+
+
+
+Aboba, et al.                Standards Track                    [Page 8]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      Id Attribute MUST NOT be included in an Access-Request; one or
+      more EAP-Server-Id Attributes MAY be included in an Access-Accept.
+
+      It should be noted that not all link layers use this name, and
+      existing EAP method implementations do not generate it.  Since the
+      NAS operates as a pass-through in EAP [RFC3748], it cannot know
+      the EAP-Server-Id before receiving it from the RADIUS server.  As
+      a result, an EAP-Server-Id Attribute sent in an Access-Request
+      MUST contain only a single NUL character.  A home RADIUS server
+      receiving an Access-Request with an EAP-Server-Id Attribute
+      containing anything other than a single NUL character MUST
+      silently discard the attribute.  In addition, the home RADIUS
+      server SHOULD include this attribute in an Access-Accept only if
+      an EAP-Server-Id Attribute was present in the Access-Request.  A
+      summary of the EAP-Server-Id Attribute format is shown below.  The
+      fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |            String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      176
+
+   Length
+
+      >=3
+
+   String
+
+      The String field is one or more octets, containing an EAP Server-
+      Id exported by the EAP method.  For details, see Appendix A of
+      [RFC5247].  A robust implementation SHOULD support the field as
+      undistinguished octets.
+
+2.5.  Mobility-Domain-Id
+
+   Description
+
+      A single Mobility-Domain-Id Attribute MAY be included in an
+      Access-Request or Accounting-Request in order to enable the NAS to
+      provide the RADIUS server with the Mobility Domain Identifier
+      (MDID), defined in Section 8.4.2.49 of [IEEE-802.11].  A summary
+      of the Mobility-Domain-Id Attribute format is shown below.  The
+      fields are transmitted from left to right.
+
+
+
+Aboba, et al.                Standards Track                    [Page 9]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |    Length     |             Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      177
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer.  The two most significant octets MUST be set to zero by
+      the sender and are ignored by the receiver; the two least
+      significant octets contain the Mobility Domain Identifier (MDID)
+      defined in Section 8.4.2.49 of [IEEE-802.11].
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |            Reserved           |   Mobility Domain Identifier  |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+2.6.  Preauth-Timeout
+
+   Description
+
+      This attribute sets the maximum number of seconds that pre-
+      authentication state is required to be kept by the NAS without
+      being utilized within a user session.  For example, when
+      [IEEE-802.11] pre-authentication is used, if a user has not
+      attempted to utilize the Pairwise Master Key (PMK) derived as a
+      result of pre-authentication within the time specified by the
+      Preauth-Timeout Attribute, the PMK MAY be discarded by the Access
+      Point.  However, once the session is underway, the Preauth-Timeout
+      Attribute has no bearing on the maximum session time for the user
+      or the maximum time during which key state may be kept prior to
+      re-authentication.  This is determined by the Session-Timeout
+      Attribute, if present.
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 10]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      A single Preauth-Timeout Attribute MAY be included within an
+      Access-Accept or CoA-Request packet.  A summary of the Preauth-
+      Timeout Attribute format is shown below.  The fields are
+      transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |    Length     |             Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value (cont)         |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      178
+
+   Length
+
+      6
+
+   Value
+
+      The field is 4 octets, containing a 32-bit unsigned integer
+      encoding the maximum time in seconds that pre-authentication state
+      should be retained by the NAS.
+
+2.7.  Network-Id-Name
+
+   Description
+
+      The Network-Id-Name Attribute is utilized by implementations of
+      IEEE-802.1X [IEEE-802.1X] to specify the name of a Network-Id
+      (NID-Name).
+
+      Unlike the IEEE 802.11 SSID (which is a maximum of 32 octets in
+      length), the NID-Name may be up to 253 octets in length.
+      Consequently, if the MAC address is included within the Called-
+      Station-Id Attribute, it is possible that there will not be enough
+      remaining space to encode the NID-Name as well.  Therefore, when
+      used with IEEE 802.1X [IEEE-802.1X], the Called-Station-Id
+      Attribute SHOULD contain only the MAC address, with the Network-
+      Id-Name Attribute used to transmit the NID-Name.  The Network-Id-
+      Name Attribute MUST NOT be used to encode the IEEE 802.11 SSID; as
+      noted in [RFC3580], the Called-Station-Id Attribute is used for
+      this purpose.
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 11]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      Zero or one Network-Id-Name Attribute is permitted within an
+      Access-Request, Access-Challenge, Access-Accept or Accounting-
+      Request packet.  When included within an Access-Request packet,
+      the Network-Id-Name Attribute represents a hint of the NID-Name to
+      which the Supplicant should be granted access.  When included
+      within an Access-Accept packet, the Network-Id-Name Attribute
+      represents the NID-Name to which the Supplicant is to be granted
+      access.  When included within an Accounting-Request packet, the
+      Network-Id-Name Attribute represents the NID-Name to which the
+      Supplicant has been granted access.
+
+      A summary of the Network-Id-Name Attribute format is shown below.
+      The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |            String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      179
+
+   Length
+
+      >=3
+
+   String
+
+      The String field is one or more octets, containing a NID-Name.
+      For details, see [IEEE-802.1X].  A robust implementation SHOULD
+      support the field as undistinguished octets.
+
+2.8.  EAPoL-Announcement
+
+   Description
+
+      The EAPoL-Announcement Attribute contains EAPoL-Announcement Type-
+      Length-Value (TLV) tuples defined within Table 11-8 of IEEE-802.1X
+      [IEEE-802.1X].  The acronym "EAPoL" stands for Extensible
+      Authentication Protocol over Local Area Network.
+
+      Zero or more EAPoL-Announcement Attributes are permitted within an
+      Access-Request, Access-Accept, Access-Challenge, Access-Reject,
+      Accounting-Request, CoA-Request, or Disconnect-Request packet.
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 12]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      When included within an Access-Request packet, EAPoL-Announcement
+      Attributes contain EAPoL-Announcement TLVs that the user sent in
+      an EAPoL-Announcement.  When included within an Access-Accept,
+      Access-Challenge, Access-Reject, CoA-Request or Disconnect-Request
+      packet, EAPoL-Announcement Attributes contain EAPoL-Announcement
+      TLVs that the NAS is to send to the user in a unicast EAPoL-
+      Announcement.  When sent within an Accounting-Request packet,
+      EAPoL-Announcement Attributes contain EAPoL-Announcement TLVs that
+      the NAS has most recently sent to the user in a unicast EAPoL-
+      Announcement.
+
+      A summary of the EAPoL-Announcement Attribute format is shown
+      below.  The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |    Length     |             String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      180
+
+   Length
+
+      >=3
+
+   String
+
+      The String field is one or more octets, containing EAPoL-
+      Announcement TLVs in the format defined in Figure 11-8 of Section
+      11.12 of [IEEE-802.1X].  Any EAPoL-Announcement TLV Type MAY be
+      included within an EAPoL-Announcement Attribute, including
+      Organizationally Specific TLVs.  If multiple EAPoL-Announcement
+      Attributes are present in a packet, their String fields MUST be
+      concatenated before being parsed for EAPoL-Announcement TLVs; this
+      allows EAPoL-Announcement TLVs longer than 253 octets to be
+      transported by RADIUS.  Similarly, EAPoL-Announcement TLVs larger
+      than 253 octets MUST be fragmented between multiple EAPoL-
+      Announcement Attributes.
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 13]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.9.  WLAN-HESSID
+
+   Description
+
+      The WLAN-HESSID Attribute contains a MAC address that identifies
+      the Homogenous Extended Service Set.  The HESSID is a globally
+      unique identifier that, in conjunction with the SSID, encoded
+      within the Called-Station-Id Attribute as described in [RFC3580],
+      may be used to provide network identification for a subscription
+      service provider network (SSPN), as described in Section 8.4.2.94
+      of [IEEE-802.11].  Zero or one WLAN-HESSID Attribute is permitted
+      within an Access-Request or Accounting-Request packet.
+
+      A summary of the WLAN-HESSID Attribute format is shown below.  The
+      fields are transmitted from left to right.
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |    Length     |          String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      181
+
+   Length
+
+      19
+
+   String
+
+      The String field is encoded in uppercase ASCII characters with the
+      octet values separated by dash characters, as described in RFC
+      3580 [RFC3580], for example, "00-10-A4-23-19-C0".
+
+2.10.  WLAN-Venue-Info
+
+   Description
+
+      The WLAN-Venue-Info Attribute identifies the category of venue
+      hosting the WLAN, as defined in Section 8.4.1.34 of [IEEE-802.11].
+      Zero or more WLAN-Venue-Info Attributes may be included in an
+      Access-Request or Accounting-Request.
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 14]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      A summary of the WLAN-Venue-Info Attribute format is shown below.
+      The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |    Length     |             Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      182
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer.  The two most significant octets MUST be set to zero by
+      the sender, and are ignored by the receiver; the two least
+      significant octets contain the Venue Group and Venue Type fields.
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |            Reserved           |  Venue Group  |  Venue Type   |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+      Venue Group
+
+         The Venue Group field is a single octet and describes the broad
+         category of the venue, e.g., "Assembly".  See Section 8.4.1.34
+         of [IEEE-802.11] for Venue Group codes and descriptions.
+
+      Venue Type
+
+         The Venue Type field is a single octet and describes the venue
+         in a finer granularity within the Venue Group, e.g., "Library".
+         See Section 8.4.1.34 of [IEEE-802.11] for Venue Type codes and
+         descriptions.
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 15]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.11.  WLAN-Venue-Language
+
+   Description
+
+      The WLAN-Venue-Language Attribute is a string encoded by
+      ISO-14962-1997 [ISO-14962-1997] that defines the language used in
+      the WLAN-Venue-Name Attribute.  Zero or more WLAN-Venue-Language
+      Attributes may be included in an Access-Request or Accounting-
+      Request, and each one indicates the language of the WLAN-Venue-
+      Name Attribute that follows it.
+
+      A summary of the WLAN-Venue-Language Attribute format is shown
+      below.  The fields are transmitted from left to right.
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |    Length     |         String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        String (cont) |
+      +-+-+-+-+-+-+-+-+
+
+   Type
+
+      183
+
+   Length
+
+      4-5
+
+   String
+
+      The String field is a two- or three-character language code
+      selected from ISO-639 [ISO-639].  A two-character language code
+      has a zero ("null" in ISO-14962-1997) appended to make it 3 octets
+      in length.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 16]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.12.  WLAN-Venue-Name
+
+   Description
+
+      The WLAN-Venue-Name Attribute provides additional metadata on the
+      Basic Service Set (BSS).  For example, this information may be
+      used to assist a user in selecting the appropriate BSS with which
+      to associate.  Zero or more WLAN-Venue-Name Attributes may be
+      included in an Access- Request or Accounting-Request in the same
+      or different languages.
+
+      A summary of the WLAN-Venue-Name Attribute format is shown below.
+      The fields are transmitted from left to right.
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |    Length     |          String...
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      184
+
+   Length
+
+      >=3
+
+   String
+
+      The String field is encoded in UTF-8 and contains the venue's
+      name.  The maximum length of this field is 252 octets.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 17]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.13.  WLAN-Reason-Code
+
+   Description
+
+      The WLAN-Reason-Code Attribute contains information on the reason
+      why a Station has been refused network access and has been
+      disassociated or de-authenticated.  This can occur due to policy
+      or for reasons related to the user's subscription.
+
+      A WLAN-Reason-Code Attribute MAY be included within an Access-
+      Reject or Disconnect-Request packet, as well as within an
+      Accounting-Request packet.  Upon receipt of an Access-Reject or
+      Disconnect-Request packet containing a WLAN-Reason-Code Attribute,
+      the WLAN-Reason-Code value is copied by the Access Point into the
+      Reason Code field of a Disassociation or Deauthentication frame
+      (see Clauses 8.3.3.4 and 8.3.3.12, respectively, in
+      [IEEE-802.11]), which is subsequently transmitted to the Station.
+
+      A summary of the WLAN-Reason-Code Attribute format is shown below.
+      The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |             Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      185
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer.  The two most significant octets MUST be set to zero by
+      the sender and are ignored by the receiver; the two least
+      significant octets contain the Reason Code values defined in Table
+      8-36 of Section 8.4.1.7 of [IEEE-802.11].
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 18]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |            Reserved           |          Reason Code          |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+2.14.  WLAN-Pairwise-Cipher
+
+   Description
+
+      The WLAN-Pairwise-Cipher Attribute contains information on the
+      pairwise ciphersuite used to establish the robust security network
+      association (RSNA) between the AP and mobile device.  A WLAN-
+      Pairwise-Cipher Attribute MAY be included within Access-Request
+      and Accounting-Request packets.
+
+      A summary of the WLAN-Pairwise-Cipher Attribute format is shown
+      below.  The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |             Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      186
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer, in Suite selector format as specified in Figure 8-187
+      within Section 8.4.2.27.2 of [IEEE-802.11], with values of OUI and
+      Suite Type drawn from Table 8-99.
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                OUI                            |  Suite Type   |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 19]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.15.  WLAN-Group-Cipher
+
+   Description
+
+      The WLAN-Group-Cipher Attribute contains information on the group
+      ciphersuite used to establish the robust security network
+      association (RSNA) between the AP and mobile device.  A WLAN-
+      Group-Cipher Attribute MAY be included within Access-Request and
+      Accounting-Request packets.
+
+      A summary of the WLAN-Group-Cipher Attribute format is shown
+      below.  The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |             Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      187
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer, in Suite selector format as specified in Figure 8-187
+      within Section 8.4.2.27.2 of [IEEE-802.11], with values of OUI and
+      Suite Type drawn from Table 8-99.
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                OUI                            |  Suite Type   |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 20]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.16.  WLAN-AKM-Suite
+
+   Description
+
+      The WLAN-AKM-Suite Attribute contains information on the
+      authentication and key management suite used to establish the
+      robust security network association (RSNA) between the AP and
+      mobile device.  A WLAN-AKM-Suite Attribute MAY be included within
+      Access-Request and Accounting-Request packets.
+
+      A summary of the WLAN-AKM-Suite Attribute format is shown below.
+      The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |             Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      188
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer, in Suite selector format as specified in Figure 8-187
+      within Section 8.4.2.27.2 of [IEEE-802.11], with values of OUI and
+      Suite Type drawn from Table 8-101:
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                OUI                            |  Suite Type   |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 21]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.17.  WLAN-Group-Mgmt-Cipher
+
+   Description
+
+      The WLAN-Group-Mgmt-Cipher Attribute contains information on the
+      group management cipher used to establish the robust security
+      network association (RSNA) between the AP and mobile device.
+
+      Zero or one WLAN-Group-Mgmt-Cipher Attribute MAY be included
+      within Access-Request and Accounting-Request packets.  The
+      presence of the Attribute indicates that the Station negotiated to
+      use management frame protection during association.
+
+      A summary of the WLAN-Group-Mgmt-Cipher Attribute format is shown
+      below.  The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |     Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      189
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer, in Suite selector format as specified in Figure 8-187
+      within Section 8.4.2.27.2 of [IEEE-802.11], with values of OUI and
+      Suite Type drawn from Table 8-99:
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                OUI                            |  Suite Type   |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 22]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+2.18.  WLAN-RF-Band
+
+   Description
+
+      The WLAN-RF-Band Attribute contains information on the radio
+      frequency (RF) band used by the Access Point for transmission and
+      reception of information to and from the mobile device.  Zero or
+      one WLAN-RF-Band Attribute MAY be included within an Access-
+      Request or Accounting-Request packet.
+
+      A summary of the WLAN-RF-Band Attribute format is shown below.
+      The fields are transmitted from left to right.
+
+       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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |     Type      |  Length       |     Value
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                 Value                |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   Type
+
+      190
+
+   Length
+
+      6
+
+   Value
+
+      The Value field is four octets, containing a 32-bit unsigned
+      integer.  The three most significant octets MUST be set to zero by
+      the sender and are ignored by the receiver; the least significant
+      octet contains the RF Band field, whose values are defined by the
+      IEEE 802.11 Band ID field (Table 8-53a of [IEEE-802.11ad])
+
+      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
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |            Reserved                           |    RF Band    |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 23]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+3.  Table of Attributes
+
+   The following table provides a guide to which attributes may be found
+   in which kinds of packets and in what quantity.
+
+   Access-  Access-  Access-  Access-
+   Request  Accept   Reject   Challenge  #   Attribute
+   0        0+       0        0        174  Allowed-Called-Station-Id
+   0-1      0-1      0        0        102   EAP-Key-Name
+   0-1      0+       0        0        175  EAP-Peer-Id
+   0-1      0+       0        0        176  EAP-Server-Id
+   0-1      0        0        0        177  Mobility-Domain-Id
+   0-1      0-1      0        0        178  Preauth-Timeout
+   0-1      0        0        0        179  Network-Id-Name
+   0+       0+       0+       0+       180  EAPoL-Announcement
+   0-1      0        0        0        181  WLAN-HESSID
+   0-1      0        0        0        182  WLAN-Venue-Info
+   0+       0        0        0        183  WLAN-Venue-Language
+   0+       0        0        0        184  WLAN-Venue-Name
+   0        0        0-1      0        185  WLAN-Reason-Code
+   0-1      0        0        0        186  WLAN-Pairwise-Cipher
+   0-1      0        0        0        187  WLAN-Group-Cipher
+   0-1      0        0        0        188  WLAN-AKM-Suite
+   0-1      0        0        0        189  WLAN-Group-Mgmt-Cipher
+   0-1      0        0        0        190  WLAN-RF-Band
+
+   CoA- Dis-  Acct-
+   Req  Req   Req  #      Attribute
+   0+    0    0+   174   Allowed-Called-Station-Id
+   0-1   0    0    102   EAP-Key-Name
+   0     0    0+   175   EAP-Peer-Id
+   0     0    0+   176   EAP-Server-Id
+   0     0    0-1  177   Mobility-Domain-Id
+   0-1   0    0    178   Preauth-Timeout
+   0     0    0-1  179   Network-Id-Name
+   0+    0+   0+   180   EAPoL-Announcement
+   0     0    0-1  181   WLAN-HESSID
+   0     0    0-1  182   WLAN-Venue-Info
+   0     0    0+   183   WLAN-Venue-Language
+   0     0    0+   184   WLAN-Venue-Name
+   0     0-1  0-1  185   WLAN-Reason-Code
+   0     0    0-1  186   WLAN-Pairwise-Cipher
+   0     0    0-1  187   WLAN-Group-Cipher
+   0     0    0-1  188   WLAN-AKM-Suite
+   0     0    0-1  189   WLAN-Group-Mgmt-Cipher
+   0     0    0-1  190   WLAN-RF-Band
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 24]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+   The following table defines the above table entries.
+
+   0     This attribute MUST NOT be present in packet.
+   0+    Zero or more instances of this attribute MAY be present in the
+         packet.
+   0-1   Zero or one instance of this attribute MAY be present in the
+         packet.
+
+4.  IANA Considerations
+
+   This document uses the RADIUS [RFC2865] namespace; see
+   <http://www.iana.org/assignments/radius-types>.  Per this
+   specification, RADIUS attribute types have been assigned for the
+   following attributes:
+
+   Attribute                        Type
+   =========                        ====
+   Allowed-Called-Station-Id        174
+   EAP-Peer-Id                      175
+   EAP-Server-Id                    176
+   Mobility-Domain-Id               177
+   Preauth-Timeout                  178
+   Network-Id-Name                  179
+   EAPoL-Announcement               180
+   WLAN-HESSID                      181
+   WLAN-Venue-Info                  182
+   WLAN-Venue-Language              183
+   WLAN-Venue-Name                  184
+   WLAN-Reason-Code                 185
+   WLAN-Pairwise-Cipher             186
+   WLAN-Group-Cipher                187
+   WLAN-AKM-Suite                   188
+   WLAN-Group-Mgmt-Cipher           189
+   WLAN-RF-Band                     190
+
+   Since this specification relies entirely on values assigned by IEEE
+   802, no registries are established for maintenance by the IANA.
+
+5.  Security Considerations
+
+   Since this document describes the use of RADIUS for purposes of
+   authentication, authorization, and accounting in IEEE 802 networks,
+   it is vulnerable to all of the threats that are present in other
+   RADIUS applications.  For a discussion of these threats, see
+   [RFC2607], [RFC2865], [RFC3162], [RFC3579], [RFC3580], and [RFC5176].
+   In particular, when RADIUS traffic is sent in the clear, the
+   attributes defined in this document can be obtained by an attacker
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 25]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+   snooping the exchange between the RADIUS client and server.  As a
+   result, RADIUS confidentiality is desirable; for a review of RADIUS
+   security and crypto-agility requirements, see [RFC6421].
+
+   While it is possible for a RADIUS server to make decisions on whether
+   to accept or reject an Access-Request based on the values of the
+   WLAN-Pairwise-Cipher, WLAN-Group-Cipher, WLAN-AKM-Suite, WLAN-Group-
+   Mgmt-Cipher, and WLAN-RF-Band Attributes, the value of doing this is
+   limited.  In general, an Access-Reject should not be necessary,
+   except where Access Points and Stations are misconfigured so as to
+   enable connections to be made with unacceptable values.  Rather than
+   rejecting access on an ongoing basis, users would be better served by
+   fixing the misconfiguration.
+
+   Where access does need to be rejected, the user should be provided
+   with an indication of why the problem has occurred, or else they are
+   likely to become frustrated.  For example, if the values of the WLAN-
+   Pairwise-Cipher, WLAN-Group-Cipher, WLAN-AKM-Suite, or WLAN-Group-
+   Mgmt-Cipher Attributes included in the Access-Request are not
+   acceptable to the RADIUS server, then a WLAN-Reason-Code Attribute
+   with a value of 29 (Requested service rejected because of service
+   provider ciphersuite or AKM requirement) SHOULD be returned in the
+   Access-Reject.  Similarly, if the value of the WLAN-RF-Band Attribute
+   included in the Access-Request is not acceptable to the RADIUS
+   server, then a WLAN-Reason-Code Attribute with a value of 11
+   (Disassociated because the information in the Supported Channels
+   element is unacceptable) SHOULD be returned in the Access-Reject.
+
+6.  References
+
+6.1.  Normative References
+
+   [IEEE-802] IEEE, "IEEE Standard for Local and Metropolitan Area
+              Networks: Overview and Architecture. Amendment 2:
+              Registration of Object Identifiers", ANSI/IEEE Std 802,
+              2001.
+
+   [IEEE-802.11]
+              IEEE, "IEEE Standard for Information technology -
+              Telecommunications and information exchange between
+              systems - Local and metropolitan area networks - Specific
+              requirements Part 11:  Wireless LAN Medium Access Control
+              (MAC) and Physical Layer (PHY) Specifications", IEEE Std
+              802.11-2012, 2012.
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 26]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+   [IEEE-802.11ad]
+              IEEE, "IEEE Standard for Information technology -
+              Telecommunications and information exchange between
+              systems - Local and metropolitan area networks - Specific
+              requirements Part 11:  Wireless LAN Medium Access Control
+              (MAC) and Physical Layer (PHY) Specifications, Amendment
+              3: Enhancements for Very High Throughput in the 60 GHz
+              Band", IEEE Std 802.11ad-2012, 2012.
+
+   [IEEE-802.1X]
+              IEEE, "IEEE Standard for Local and metropolitan area
+              networks - Port-Based Network Access Control", IEEE Std
+              802.1X-2010, February 2010.
+
+   [ISO-639]  ISO, "Codes for the Representation of Names of Languages",
+              ISO 639.
+
+   [ISO-14962-1997]
+              ISO, "Space data and information transfer systems - ASCII
+              encoded English", 1997.
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [RFC2865]  Rigney, C., Willens, S., Rubens, A., and W. Simpson,
+              "Remote Authentication Dial In User Service (RADIUS)", RFC
+              2865, June 2000.
+
+   [RFC4072]  Eronen, P., Ed., Hiller, T., and G. Zorn, "Diameter
+              Extensible Authentication Protocol (EAP) Application", RFC
+              4072, August 2005.
+
+   [RFC5247]  Aboba, B., Simon, D., and P. Eronen, "Extensible
+              Authentication Protocol (EAP) Key Management Framework",
+              RFC 5247, August 2008.
+
+6.2.  Informative References
+
+   [RFC2607]  Aboba, B. and J. Vollbrecht, "Proxy Chaining and Policy
+              Implementation in Roaming", RFC 2607, June 1999.
+
+   [RFC3162]  Aboba, B., Zorn, G., and D. Mitton, "RADIUS and IPv6", RFC
+              3162, August 2001.
+
+   [RFC3579]  Aboba, B. and P. Calhoun, "RADIUS (Remote Authentication
+              Dial In User Service) Support For Extensible
+              Authentication Protocol (EAP)", RFC 3579, September 2003.
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 27]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+   [RFC3580]  Congdon, P., Aboba, B., Smith, A., Zorn, G., and J. Roese,
+              "IEEE 802.1X Remote Authentication Dial In User Service
+              (RADIUS) Usage Guidelines", RFC 3580, September 2003.
+
+   [RFC3748]  Aboba, B., Blunk, L., Vollbrecht, J., Carlson, J., and H.
+              Levkowetz, Ed., "Extensible Authentication Protocol
+              (EAP)", RFC 3748, June 2004.
+
+   [RFC5176]  Chiba, M., Dommety, G., Eklund, M., Mitton, D., and B.
+              Aboba, "Dynamic Authorization Extensions to Remote
+              Authentication Dial In User Service (RADIUS)", RFC 5176,
+              January 2008.
+
+   [RFC6421]  Nelson, D., Ed., "Crypto-Agility Requirements for Remote
+              Authentication Dial-In User Service (RADIUS)", RFC 6421,
+              November 2011.
+
+7.  Acknowledgments
+
+   The authors would like to acknowledge Maximilian Riegel, Dorothy
+   Stanley, Yoshihiro Ohba, and the contributors to the IEEE 802.1 and
+   IEEE 802.11 reviews of this document, for useful discussions.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 28]
+\f
+RFC 7268             RADIUS Attributes for IEEE 802            July 2014
+
+
+Authors' Addresses
+
+   Bernard Aboba
+   Microsoft Corporation
+   One Microsoft Way
+   Redmond, WA 98052
+   US
+
+   EMail: bernard_aboba@hotmail.com
+
+
+   Jouni Malinen
+
+   EMail: j@w1.fi
+
+
+   Paul Congdon
+   Tallac Networks
+   6528 Lonetree Blvd.
+   Rocklin, CA 95765
+   US
+
+   Phone: +19167576350
+   EMail: paul.congdon@tallac.com
+
+
+   Joseph Salowey
+   Cisco Systems
+
+   EMail: jsalowey@cisco.com
+
+
+   Mark Jones
+   Azuca Systems
+
+   EMail:  mark@azu.ca
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Aboba, et al.                Standards Track                   [Page 29]
+\f
diff --git a/doc/rfc/rfc7542.txt b/doc/rfc/rfc7542.txt
new file mode 100644 (file)
index 0000000..7413474
--- /dev/null
@@ -0,0 +1,1683 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF)                          A. DeKok
+Request for Comments: 7542                                    FreeRADIUS
+Obsoletes: 4282                                                 May 2015
+Category: Standards Track
+ISSN: 2070-1721
+
+
+                     The Network Access Identifier
+
+Abstract
+
+   In order to provide inter-domain authentication services, it is
+   necessary to have a standardized method that domains can use to
+   identify each other's users.  This document defines the syntax for
+   the Network Access Identifier (NAI), the user identifier submitted by
+   the client prior to accessing resources.  This document is a revised
+   version of RFC 4282.  It addresses issues with international
+   character sets and makes a number of other corrections to RFC 4282.
+
+Status of This Memo
+
+   This is an Internet Standards Track document.
+
+   This document is a product of the Internet Engineering Task Force
+   (IETF).  It represents the consensus of the IETF community.  It has
+   received public review and has been approved for publication by the
+   Internet Engineering Steering Group (IESG).  Further information on
+   Internet Standards is available in Section 2 of RFC 5741.
+
+   Information about the current status of this document, any errata,
+   and how to provide feedback on it may be obtained at
+   http://www.rfc-editor.org/info/rfc7542.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                    [Page 1]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+Copyright Notice
+
+   Copyright (c) 2015 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+   This document may contain material from IETF Documents or IETF
+   Contributions published or made publicly available before November
+   10, 2008.  The person(s) controlling the copyright in some of this
+   material may not have granted the IETF Trust the right to allow
+   modifications of such material outside the IETF Standards Process.
+   Without obtaining an adequate license from the person(s) controlling
+   the copyright in such materials, this document may not be modified
+   outside the IETF Standards Process, and derivative works of it may
+   not be created outside the IETF Standards Process, except to format
+   it for publication as an RFC or to translate it into languages other
+   than English.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                    [Page 2]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+Table of Contents
+
+   1. Introduction ....................................................4
+      1.1. Terminology ................................................6
+      1.2. Requirements Language ......................................7
+      1.3. Purpose ....................................................7
+      1.4. Motivation .................................................9
+   2. NAI Definition .................................................10
+      2.1. UTF-8 Syntax and Normalization ............................10
+      2.2. Formal Syntax .............................................11
+      2.3. NAI Length Considerations .................................11
+      2.4. Support for Username Privacy ..............................12
+      2.5. International Character Sets ..............................13
+      2.6. The Normalization Process .................................14
+           2.6.1. Issues with the Normalization Process ..............15
+      2.7. Use in Other Protocols ....................................16
+      2.8. Using the NAI Format for Other Identifiers ................17
+   3. Routing inside of AAA Systems ..................................18
+      3.1. Compatibility with Email Usernames ........................19
+      3.2. Compatibility with DNS ....................................20
+      3.3. Realm Construction ........................................20
+           3.3.1. Historical Practices ...............................21
+      3.4. Examples ..................................................22
+   4. Security Considerations ........................................23
+      4.1. Correlation of Identities over Time and Protocols .........23
+      4.2. Multiple Identifiers ......................................24
+   5. Administration of Names ........................................25
+   6. References .....................................................26
+      6.1. Normative References ......................................26
+      6.2. Informative References ....................................26
+   Appendix A. Changes from RFC 4282 .................................29
+   Acknowledgments ...................................................30
+   Author's Address ..................................................30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                    [Page 3]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+1.  Introduction
+
+   Considerable interest exists for a set of features that fit within
+   the general category of inter-domain authentication, or "roaming
+   capability" for network access, including dialup Internet users,
+   Virtual Private Network (VPN) usage, wireless LAN authentication, and
+   other applications.
+
+   By "inter-domain authentication", this document refers to situations
+   where a user has authentication credentials at one "home" domain but
+   is able to present them at a second "visited" domain to access
+   certain services at the visited domain.  The two domains generally
+   have a pre-existing relationship, so that the credentials can be
+   passed from the visited domain to the home domain for verification.
+   The home domain typically responds with a permit/deny response, which
+   may also include authorization parameters that the visited domain is
+   expected to enforce on the user.
+
+   That is, the "roaming" scenario involves a user visiting, or
+   "roaming" to, a non-home domain and requesting the use of services at
+   that visited domain.
+
+   Interested parties have included the following:
+
+   *  Regional Internet Service Providers (ISPs) operating within a
+      particular state or province, looking to combine their efforts
+      with those of other regional providers to offer dialup service
+      over a wider area.
+
+   *  Telecommunications companies who wish to combine their operations
+      with those of one or more companies in other areas or nations, in
+      order to offer more comprehensive network access service in areas
+      where there is no native service (e.g., in another country).
+
+   *  Wireless LAN hotspots providing service to one or more ISPs.
+
+   *  Businesses desiring to offer their employees a comprehensive
+      package of dialup services on a global basis.  Those services may
+      include Internet access as well as secure access to corporate
+      intranets via a VPN, enabled by tunneling protocols such as the
+      Point-to-Point Tunneling Protocol (PPTP) [RFC2637], the Layer 2
+      Forwarding (L2F) protocol [RFC2341], the Layer 2 Tunneling
+      Protocol (L2TP) [RFC2661], and the IPsec tunnel mode [RFC4301].
+
+   *  Other protocols that are interested in leveraging the users'
+      credentials in order to take advantage of an existing
+      authentication framework.
+
+
+
+
+DeKok                        Standards Track                    [Page 4]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   In order to enhance the interoperability of these services, it is
+   necessary to have a standardized method for identifying users.  This
+   document defines syntax for the Network Access Identifier (NAI).
+   Examples of implementations that use the NAI, and descriptions of its
+   semantics, can be found in [RFC2194].
+
+   When the NAI was defined for network access, it had the side effect
+   of defining an identifier that could be used in non-AAA systems.
+   Some non-AAA systems defined identifiers that were compatible with
+   the NAI, and deployments used the NAI.  This process simplified the
+   management of credentials, by reusing the same credential in multiple
+   situations.  Protocols that reuse the same credential or the same
+   identifier format can benefit from this simplified management.  The
+   alternative is to have protocol-specific credentials or identifier
+   formats, which increases cost to both the user and the administrator.
+
+   There are privacy implications to using one identifier across
+   multiple protocols.  See Sections 2.7 and 4 for further discussion of
+   this topic.
+
+   The goal of this document is to define the format of an identifier
+   that can be used in many protocols.  A protocol may transport an
+   encoded version of the NAI (e.g., '.' as %2E).  However, the
+   definition of the NAI is protocol independent.  The goal of this
+   document is to encourage the widespread adoption of the NAI format.
+   This adoption will decrease the work required to leverage
+   identification and authentication in other protocols.  It will also
+   decrease the complexity of non-AAA systems for end users and
+   administrators.
+
+   This document only suggests that the NAI format be used; it does not
+   require such use.  Many protocols already define their own identifier
+   formats.  Some of these are incompatible with the NAI, while others
+   allow the NAI in addition to non-NAI identifiers.  The definition of
+   the NAI in this document has no requirements on protocol
+   specifications, implementations, or deployments.
+
+   However, this document suggests that using one standard identifier
+   format is preferable to using multiple incompatible identifier
+   formats.  Where identifiers need to be used in new protocols and/or
+   specifications, it is RECOMMENDED that the format of the NAI be used.
+   That is, the interpretation of the identifier is context specific,
+   while the format of the identifier remains the same.  These issues
+   are discussed in more detail in Section 2.8, below.
+
+
+
+
+
+
+
+DeKok                        Standards Track                    [Page 5]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   The recommendation for a standard identifier format is not a
+   recommendation that each user have one universal identifier.  In
+   contrast, this document allows for the use of multiple identifiers
+   and recommends the use of anonymous identifiers where those
+   identifiers are publicly visible.
+
+   This document is a revised version of [RFC4282], which originally
+   defined internationalized NAIs.  Differences and enhancements
+   compared to that document are listed in Appendix A.
+
+1.1.  Terminology
+
+   This document frequently uses the following terms:
+
+   "Local" or "Localized" Text
+
+      "Local" or "localized" text is text that is in either non-UTF-8 or
+      non-normalized form.  The character set, encoding, and locale are
+      (in general) unknown to Authentication, Authorization, and
+      Accounting (AAA) network protocols.  The client that "knows" the
+      locale may have a different concept of this text than other AAA
+      entities, which do not know the same locale.
+
+   Network Access Identifier
+
+      The Network Access Identifier (NAI) is a common format for user
+      identifiers submitted by a client during authentication.  The
+      purpose of the NAI is to allow a user to be associated with an
+      account name, as well as to assist in the routing of the
+      authentication request across multiple domains.  Please note that
+      the NAI may not necessarily be the same as the user's email
+      address or the user identifier submitted in an application-layer
+      authentication.
+
+   Network Access Server
+
+      The Network Access Server (NAS) is the device that clients connect
+      to in order to get access to the network.  In PPTP terminology,
+      this is referred to as the PPTP Access Concentrator (PAC), and in
+      L2TP terminology, it is referred to as the L2TP Access
+      Concentrator (LAC).  In IEEE 802.11, it is referred to as an
+      Access Point.
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                    [Page 6]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   Roaming Capability
+
+      Roaming capability can be loosely defined as the ability to use
+      any one of multiple Internet Service Providers (ISPs), while
+      maintaining a formal customer-vendor relationship with only one.
+      Examples of cases where roaming capability might be required
+      include ISP "confederations" and ISP-provided corporate network
+      access support.
+
+   Normalization or Canonicalization
+
+      These terms are defined in Section 4 of [RFC6365]; those
+      definitions are incorporated here by reference.
+
+   Locale
+
+      This term is defined in [RFC6365], Section 8; that definition is
+      incorporated here by reference.
+
+   Tunneling Service
+
+      A tunneling service is any network service enabled by tunneling
+      protocols such as PPTP, L2F, L2TP, and IPsec tunnel mode.  One
+      example of a tunneling service is secure access to corporate
+      intranets via a Virtual Private Network (VPN).
+
+1.2.  Requirements Language
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
+   "OPTIONAL" in this document are to be interpreted as described in
+   [RFC2119].
+
+1.3.  Purpose
+
+   As described in [RFC2194], there are a number of providers offering
+   network access services, and essentially all Internet Service
+   Providers are involved in roaming consortia.
+
+   In order to be able to offer roaming capability, one of the
+   requirements is to be able to identify the user's home authentication
+   server.  For use in roaming, this function is accomplished via the
+   Network Access Identifier (NAI) submitted by the user to the NAS in
+   the initial network authentication.  It is also expected that NASes
+   will use the NAI as part of the process of opening a new tunnel, in
+   order to determine the tunnel endpoint.
+
+
+
+
+
+DeKok                        Standards Track                    [Page 7]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   This document suggests that other protocols can take advantage of the
+   NAI format.  Many protocols include authentication capabilities,
+   including defining their own identifier formats.  These identifiers
+   can then end up being transported in AAA protocols, so that the
+   originating protocols can leverage AAA for user authentication.
+   There is therefore a need for a definition of a user identifier that
+   can be used in multiple protocols.
+
+   While the NAI is defined herein, it should be noted that existing
+   protocols and deployments do not always use it.  AAA systems MUST
+   therefore be able to handle user identifiers that are not in the NAI
+   format.  The process by which that is done is outside of the scope of
+   this document.
+
+   Non-AAA systems can accept user identifiers in forms other than the
+   NAI.  This specification does not forbid that practice.  It only
+   codifies the format and interpretation of the NAI.  This document
+   cannot change existing protocols or practices.  It can, however,
+   suggest that using a consistent form for a user identifier is of
+   benefit to the community.
+
+   This document does not make any protocol-specific definitions for an
+   identifier format, and it does not make changes to any existing
+   protocol.  Instead, it defines a protocol-independent form for the
+   NAI.  It is hoped that the NAI is a user identifier that can be used
+   in multiple protocols.
+
+   Using a common identifier format simplifies protocols requiring
+   authentication, as they no longer need to specify a protocol-specific
+   format for user identifiers.  It increases security, as multiple
+   identifier formats allow attackers to make contradictory claims
+   without being detected (see Section 4.2 for further discussion of
+   this topic).  It simplifies deployments, as a user can have one
+   identifier in multiple contexts, which allows them to be uniquely
+   identified, so long as that identifier is itself protected against
+   unauthorized access.
+
+   In short, having a standard is better than having no standard at all.
+
+
+
+
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                    [Page 8]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+1.4.  Motivation
+
+   The changes from [RFC4282] are listed in detail in Appendix A.
+   However, some additional discussion is appropriate to motivate those
+   changes.
+
+   The motivation to revise [RFC4282] began with internationalization
+   concerns raised in the context of [EDUROAM].  Section 2.1 of
+   [RFC4282] defines ABNF for realms and limits the realm grammar to
+   English letters, digits, and the hyphen "-" character.  The intent
+   appears to have been to encode, compare, and transport realms with
+   the Punycode [RFC3492] encoding form as described in [RFC5891].
+   There are a number of problems with this approach:
+
+   *  The [RFC4282] ABNF is not aligned with internationalization
+      of DNS.
+
+   *  The requirement in Section 2.1 of [RFC4282] that realms are ASCII
+      conflicts with the Extensible Authentication Protocol (EAP) as
+      defined in [RFC3748], and RADIUS, which are both 8-bit clean, and
+      which both recommend the use of UTF-8 for identifiers.
+
+   *  Section 2.4 of [RFC4282] required mappings that are language
+      specific and that are nearly impossible for intermediate nodes to
+      perform correctly without information about that language.
+
+   *  Section 2.4 of [RFC4282] requires normalization of usernames,
+      which may conflict with local system or administrative
+      requirements.
+
+   *  The recommendations in Section 2.4 of [RFC4282] for treatment of
+      bidirectional characters have proven to be unworkable.
+
+   *  The prohibition of the use of unassigned code points in
+      Section 2.4 of [RFC4282] effectively prohibits support for new
+      scripts.
+
+   *  No Authentication, Authorization, and Accounting (AAA) client,
+      proxy, or server has implemented any of the requirements in
+      Section 2.4 of [RFC4282], among other sections.
+
+   With international roaming growing in popularity, it is important for
+   these issues to be corrected in order to provide robust and
+   interoperable network services.
+
+   Furthermore, this document was motivated by a desire to codify
+   existing practice related to the use of the NAI format and to
+   encourage widespread use of the format.
+
+
+
+DeKok                        Standards Track                    [Page 9]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+2.  NAI Definition
+
+2.1.  UTF-8 Syntax and Normalization
+
+   UTF-8 characters can be defined in terms of octets using the
+   following ABNF [RFC5234], taken from [RFC3629]:
+
+   UTF8-xtra-char  =   UTF8-2 / UTF8-3 / UTF8-4
+
+   UTF8-2          =   %xC2-DF UTF8-tail
+
+   UTF8-3          =   %xE0 %xA0-BF UTF8-tail /
+                       %xE1-EC 2( UTF8-tail ) /
+                       %xED %x80-9F UTF8-tail /
+                       %xEE-EF 2( UTF8-tail )
+
+   UTF8-4          =   %xF0 %x90-BF 2( UTF8-tail ) /
+                       %xF1-F3 3( UTF8-tail ) /
+                       %xF4 %x80-8F 2( UTF8-tail )
+
+   UTF8-tail       =   %x80-BF
+
+   These are normatively defined in [RFC3629] but are repeated in this
+   document for reasons of convenience.
+
+   See [RFC5198] and Section 2.6 of this specification for a discussion
+   of normalization.  Strings that are not Normal Form Composed (NFC)
+   are not valid NAIs and SHOULD NOT be treated as such.
+   Implementations that expect to receive an NAI but that instead
+   receive non-normalized (but otherwise valid) UTF-8 strings instead
+   SHOULD attempt to create a local version of the NAI, which is
+   normalized from the input identifier.  This local version can then be
+   used for local processing.  This local version of the identifier MUST
+   NOT be used outside of the local context.
+
+   Where protocols carry identifiers that are expected to be transported
+   over a AAA protocol, it is RECOMMENDED that the identifiers be in NAI
+   format.  Where the identifiers are not in the NAI format, it is up to
+   the AAA systems to discover this and to process them.  This document
+   does not suggest how that is done.  However, existing practice
+   indicates that it is possible.
+
+   As internationalized domain names become more widely used, existing
+   practices are likely to become inadequate.  This document therefore
+   defines the NAI, which is a user identifier format that can correctly
+   deal with internationalized identifiers.
+
+
+
+
+
+DeKok                        Standards Track                   [Page 10]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+2.2.  Formal Syntax
+
+   The grammar for the NAI is given below, described in Augmented
+   Backus-Naur Form (ABNF) as documented in [RFC5234].
+
+   nai            =   utf8-username
+   nai            =/  "@" utf8-realm
+   nai            =/  utf8-username "@" utf8-realm
+
+   utf8-username  =  dot-string
+
+   dot-string     = string *("." string)
+   string         = 1*utf8-atext
+
+   utf8-atext     =  ALPHA / DIGIT /
+                     "!" / "#" /
+                     "$" / "%" /
+                     "&" / "'" /
+                     "*" / "+" /
+                     "-" / "/" /
+                     "=" / "?" /
+                     "^" / "_" /
+                     "`" / "{" /
+                     "|" / "}" /
+                     "~" /
+                     UTF8-xtra-char
+
+   utf8-realm     =  1*( label "." ) label
+
+   label          =  utf8-rtext *(ldh-str)
+   ldh-str        =  *( utf8-rtext / "-" ) utf8-rtext
+   utf8-rtext     =  ALPHA / DIGIT / UTF8-xtra-char
+
+2.3.  NAI Length Considerations
+
+   Devices handling NAIs MUST support an NAI length of at least
+   72 octets.  Devices SHOULD support an NAI length of 253 octets.
+   However, the following implementation issues should be considered:
+
+   *  NAI octet length constraints may impose a more severe constraint
+      on the number of UTF-8 characters.
+
+   *  NAIs are often transported in the User-Name attribute of the
+      Remote Authentication Dial-In User Service (RADIUS) protocol.
+      Unfortunately, [RFC2865], Section 5.1 states that "the ability to
+      handle at least 63 octets is recommended."  As a result, it may
+      not be possible to transfer NAIs beyond 63 octets through all
+      devices.  In addition, since only a single User-Name attribute may
+
+
+
+DeKok                        Standards Track                   [Page 11]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+      be included in a RADIUS message and the maximum attribute length
+      is 253 octets, RADIUS is unable to support NAI lengths beyond
+      253 octets.
+
+   *  NAIs can also be transported in the User-Name attribute of
+      Diameter [RFC6733], which supports content lengths up to
+      2^24 - 9 octets.  As a result, NAIs processed only by Diameter
+      nodes can be very long.  However, an NAI transported over Diameter
+      may eventually be translated to RADIUS, in which case the above
+      limitations will apply.
+
+   *  NAIs may be transported in other protocols.  Each protocol can
+      have its own limitations on maximum NAI length.
+
+   The above criteria should permit the widest use and widest possible
+   interoperability of the NAI.
+
+2.4.  Support for Username Privacy
+
+   Interpretation of the username part of the NAI depends on the realm
+   in question.  Therefore, the utf8-username portion SHOULD be treated
+   as opaque data when processed by nodes that are not a part of the
+   home domain for that realm.
+
+   That is, the only domain that is capable of interpreting the meaning
+   of the utf8-username portion of the NAI is the home domain.  Any
+   third-party domains cannot form any conclusions about the
+   utf8-username and cannot decode it into subfields.  For example, it
+   may be used as "firstname.lastname", or it may be entirely digits, or
+   it may be a random hex identifier.  There is simply no way (and no
+   reason) for any other domain to interpret the utf8-username field as
+   having any meaning whatsoever.
+
+   In some situations, NAIs are used together with a separate
+   authentication method that can transfer the username part in a more
+   secure manner to increase privacy.  In this case, NAIs MAY be
+   provided in an abbreviated form by omitting the username part.
+   Omitting the username part is RECOMMENDED over using a fixed username
+   part, such as "anonymous", since including a fixed username part is
+   ambiguous as to whether or not the NAI refers to a single user.
+   However, current practice is to use the username "anonymous" instead
+   of omitting the username part.  This behavior is also permitted.
+
+   The most common use case of omitting or obfuscating the username part
+   is with TLS-based EAP methods such as Tunneled Transport Layer
+   Security (TTLS) [RFC5281].  Those methods allow for an "outer"
+   identifier, which is typically an anonymous "@realm".  This outer
+   identifier allows the authentication request to be routed from a
+
+
+
+DeKok                        Standards Track                   [Page 12]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   visited domain to a home domain.  At the same time, the username part
+   is kept confidential from the visited network.  The protocol provides
+   for an "inner" authentication exchange, in which a full identifier is
+   used to authenticate a user.
+
+   That scenario offers the best of both worlds.  An anonymous NAI can
+   be used to route authentication to the home domain, and the home
+   domain has sufficient information to identify and authenticate users.
+
+   However, some protocols do not support authentication methods that
+   allow for "inner" and "outer" exchanges.  Those protocols are limited
+   to using an identifier that is publicly visible.  It is therefore
+   RECOMMENDED that such protocols use ephemeral identifiers.  We
+   recognize that this practice is not currently used and will likely be
+   difficult to implement.
+
+   Similar to the anonymous user, there may be situations where portions
+   of the realm are sensitive.  For those situations, it is RECOMMENDED
+   that the sensitive portion of the realm also be omitted (e.g., to use
+   "@example.com" instead of "@sensitive.example.com", or
+   "anonymous@sensitive.example.com").  The home domain is authoritative
+   for users in all subdomains and can (if necessary) route the
+   authentication request to the appropriate subsystem within the home
+   domain.
+
+   For roaming purposes, it is typically necessary to locate the
+   appropriate backend authentication server for the given NAI before
+   the authentication conversation can proceed.  As a result,
+   authentication routing is impossible unless the realm portion is
+   available and is in a well-known format.
+
+2.5.  International Character Sets
+
+   This specification allows both international usernames and realms.
+   International usernames are based on the use of Unicode characters,
+   encoded as UTF-8.  Internationalization of the username portion of
+   the NAI is based on the "Internationalized Email Headers" [RFC6532]
+   extensions to the "local-part" portion of email addresses [RFC5322].
+
+   In order to ensure a canonical representation, characters of the
+   realm portion in an NAI MUST match the ABNF in this specification as
+   well as the requirements specified in [RFC5891].  In practice, these
+   requirements consist of the following item:
+
+   *  Realms MUST be of the form that can be registered as a Fully
+      Qualified Domain Name (FQDN) within the DNS.
+
+
+
+
+
+DeKok                        Standards Track                   [Page 13]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   This list is significantly shorter and simpler than the list in
+   Section 2.4 of [RFC4282].  The form suggested in [RFC4282] depended
+   on intermediate nodes performing canonicalizations based on
+   insufficient information, which meant that the form was not
+   canonical.
+
+   Specifying the realm requirement as above means that the requirements
+   depend on specifications that are referenced here, rather than copied
+   here.  This allows the realm definition to be updated when the
+   referenced documents change, without requiring a revision of this
+   specification.
+
+   One caveat on the above recommendation is the issues noted in
+   [RFC6912].  That document notes that there are additional
+   restrictions around DNS registration that forbid some code points
+   from being valid in a DNS U-label.  These restrictions cannot be
+   expressed algorithmically.
+
+   For this specification, that caveat means the following:
+   Realms not matching the above ABNF are not valid NAIs.  However, some
+   realms that do match the ABNF are still invalid NAIs.  That is,
+   matching the ABNF is a necessary, but not sufficient, requirement for
+   an NAI.
+
+   In general, the above requirement means following the requirements
+   specified in [RFC5891].
+
+2.6.  The Normalization Process
+
+   Conversion to Unicode as well as normalization SHOULD be performed by
+   edge systems (e.g., laptops, desktops, smart phones, etc.) that take
+   "local" text as input.  These edge systems are best suited to
+   determine the user's intent and can best convert from "local" text to
+   a normalized form.
+
+   Other AAA systems such as proxies do not have access to locale and
+   character set information that is available to edge systems.
+   Therefore, they may not always be able to convert local input to
+   Unicode.
+
+   That is, all processing of NAIs from "local" character sets and
+   locales to UTF-8 SHOULD be performed by edge systems, prior to the
+   NAIs entering the AAA system.  Inside of a AAA system, NAIs are sent
+   over the wire in their canonical form, and this canonical form is
+   used for all NAI and/or realm comparisons.
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 14]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   Copying of localized text into fields that can subsequently be placed
+   into the RADIUS User-Name attribute is problematic.  This practice
+   can result in a AAA proxy encountering non-UTF-8 characters within
+   what it expects to be an NAI.  An example of this requirement is
+   Section 2.1 of [RFC3579], which states:
+
+      the NAS MUST copy the contents of the Type-Data field of the
+      EAP-Response/Identity received from the peer into the User-Name
+      attribute
+
+   As a result, AAA proxies expect the contents of the
+   EAP-Response/Identity sent by an EAP supplicant to consist of UTF-8
+   characters, not localized text.  Using localized text in AAA username
+   or identity fields means that realm routing becomes difficult or
+   impossible.
+
+   In contrast to Section 2.4 of [RFC4282], AAA systems are now expected
+   to perform NAI comparisons, matching, and AAA routing based on the
+   NAI as it is received.  This specification provides a canonical
+   representation, ensures that intermediate AAA systems such as proxies
+   are not required to perform translations, and can be expected to work
+   through AAA systems that are unaware of international character sets.
+
+   In an ideal world, the following requirements would be widely
+   implemented:
+
+   *  Edge systems using "localized" text SHOULD normalize the NAI prior
+      to it being used as an identifier in an authentication protocol.
+
+   *  AAA systems SHOULD NOT normalize the NAI, as they may not have
+      sufficient information to perform the normalization.
+
+   There are issues with this approach, however.
+
+2.6.1.  Issues with the Normalization Process
+
+   The requirements in the preceding section are not implemented today.
+   For example, most EAP implementations use a user identifier that is
+   passed to them from some other local system.  This identifier is
+   treated as an opaque blob and is placed as is into the EAP Identity
+   field.  Any subsequent system that receives that identifier is
+   assumed to be able to understand and process it.
+
+   This opaque blob unfortunately can contain localized text, which
+   means that the AAA systems have to process that text.
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 15]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   These limitations have the following theoretical and practical
+   implications:
+
+   *  Edge systems used today generally do not normalize the NAI.
+
+   *  Therefore, AAA systems SHOULD attempt to normalize the NAI.
+
+   The suggestions above contradict the suggestions in the previous
+   section.  This is the reality of imperfect protocols.
+
+   Where the user identifier can be normalized, or determined to be in
+   normal form, the normal form MUST be used as the NAI.  In all other
+   circumstances, the user identifier MUST NOT be treated as an NAI.
+   That data is still, however, a user identifier.  AAA systems MUST NOT
+   fail authentication simply because the user identifier is not an NAI.
+
+   That is, when the realm portion of the NAI is not recognized by a AAA
+   server, it SHOULD try to normalize the NAI into NFC form.  That
+   normalized form can then be used to see if the realm matches a known
+   realm.  If no match is found, the original form of the NAI SHOULD be
+   used in all subsequent processing.
+
+   The AAA server may also convert realms to Punycode and perform all
+   realm comparisons on the resulting Punycode strings.  This conversion
+   follows the recommendations above but may have different operational
+   effects and failure modes.
+
+2.7.  Use in Other Protocols
+
+   As noted earlier, the NAI format can be used in other, non-AAA
+   protocols.  It is RECOMMENDED that the definition given here be used
+   unchanged.  Using other definitions for user identifiers may hinder
+   interoperability, along with the user's ability to authenticate
+   successfully.  It is RECOMMENDED that protocols requiring the use of
+   a user identifier use the NAI format.
+
+   This document cannot require other protocols to use the NAI format
+   for user identifiers.  Their needs are unknown and, at this time,
+   unknowable.  This document suggests that interoperability and
+   inter-domain authentication are useful and should be encouraged.
+
+   Where a protocol is 8-bit clean, it can likely transport the NAI as
+   is, without further modification.
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 16]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   Where a protocol is not 8-bit clean, it cannot transport the NAI as
+   is.  Instead, this document presumes that a protocol-specific
+   transport layer takes care of encoding the NAI on input to the
+   protocol and decoding it when the NAI exits the protocol.  The
+   encoded or escaped version of the NAI is not a valid NAI and MUST NOT
+   be presented to the AAA system.
+
+   For example, HTTP carries user identifiers but escapes the '.'
+   character as "%2E" (among others).  When HTTP is used to transport
+   the NAI "fred@example.com", the data as transported will be in the
+   form "fred@example%2Ecom".  That data exists only within HTTP and has
+   no relevance to any AAA system.
+
+   Any comparison, validation, or use of the NAI MUST be done on its
+   unescaped (i.e., utf8-clean) form.
+
+2.8.  Using the NAI Format for Other Identifiers
+
+   As discussed in Section 1, above, it is RECOMMENDED that the NAI
+   format be used as the standard format for user identifiers.  This
+   section discusses that use in more detail.
+
+   It is often useful to create new identifiers for use in specific
+   contexts.  These identifiers may have a number of different
+   properties, most of which are unimportant to this document.  The
+   goal of this document is to create identifiers that are to be in a
+   well-known format and that will have namespaces.  The NAI format fits
+   these requirements.
+
+   One example of such use is the "private user identity", which is an
+   identifier defined by the 3rd Generation Partnership Project (3GPP).
+   That identifier is used to uniquely identify the user to the network.
+   The identifier is used for authorization, authentication, accounting,
+   administration, etc.  The "private user identity" is globally unique
+   and is defined by the home network operator.  The format of the
+   identifier is explicitly the NAI, as stated by Section 13.3 of
+   [3GPP]:
+
+      The private user identity shall take the form of an NAI, and shall
+      have the form username@realm as specified in clause 2.1 of IETF
+      RFC 4282
+
+   For 3GPP, the "username" portion is a unique identifier that is
+   derived from device-specific information.  The "realm" portion is
+   composed of information about the home network, followed by the base
+   string "3gppnetwork.org" (e.g.,
+   234150999999999@ims.mnc015.mcc234.3gppnetwork.org).
+
+
+
+
+DeKok                        Standards Track                   [Page 17]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   This format as defined by 3GPP ensures that the identifier is
+   globally unique, as it is based on the "3gppnetwork.org" domain.  It
+   ensures that the "realm" portion is specific to a particular home
+   network (or organization), via the "ims.mnc015.mcc234" prefix to the
+   realm.  Finally, it ensures that the "username" portion follows a
+   well-known format.
+
+   This document suggests that the NAI format be used for all new
+   specifications and/or protocols where a user identifier is required.
+   Where the username portions need to be created with subfields, a
+   well-known and documented method, as has been done with 3GPP, is
+   preferred to ad hoc methods.
+
+3.  Routing inside of AAA Systems
+
+   Many AAA systems use the "utf8-realm" portion of the NAI to route
+   requests within a AAA proxy network.  The semantics of this operation
+   involves a logical AAA routing table, where the "utf8-realm" portion
+   acts as a key, and the values stored in the table are one or more
+   "next hop" AAA servers.
+
+   Intermediate nodes MUST use the "utf8-realm" portion of the NAI
+   without modification to perform this lookup.  As noted earlier,
+   intermediate nodes may not have access to the same locale information
+   as the system that injected the NAI into the AAA routing systems.
+   Therefore, almost all "case insensitive" comparisons can be wrong.
+   Where the "utf8-realm" is entirely ASCII, current AAA systems
+   sometimes perform case-insensitive matching on realms.  This method
+   MAY be continued, as it has been shown to work in practice.
+
+   Many existing non-AAA systems have user identifiers that are similar
+   in format to the NAI but that are not compliant with this
+   specification.  For example, they may use non-NFC form, or they may
+   have multiple "@" characters in the user identifier.  Intermediate
+   nodes SHOULD normalize non-NFC identifiers to NFC, prior to looking
+   up the "utf8-realm" in the logical routing table.  Intermediate nodes
+   MUST NOT modify the identifiers that they forward.  The data as
+   entered by the user is inviolate.
+
+   The "utf8-realm" provisioned in the logical AAA routing table SHOULD
+   be provisioned to the proxy prior to it receiving any AAA traffic.
+   The "utf8-realm" SHOULD be supplied by the "next hop" or "home"
+   system that also supplies the routing information necessary for
+   packets to reach the next hop.
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 18]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   This "next hop" information may be any of, or all of, the following
+   information: IP address, port, RADIUS shared secret, TLS certificate,
+   DNS host name, or instruction to use dynamic DNS discovery (i.e.,
+   look up a record in the "utf8-realm" domain).  This list is not
+   exhaustive and may be extended by future specifications.
+
+   It is RECOMMENDED to use the entirety of the "utf8-realm" for the
+   routing decisions.  However, AAA systems MAY use a portion of the
+   "utf8-realm" portion, so long as that portion is a valid "utf8-realm"
+   and is handled as above.  For example, routing "fred@example.com" to
+   a "com" destination is forbidden, because "com" is not a valid
+   "utf8-realm".  However, routing "fred@sales.example.com" to the
+   "example.com" destination is permissible.
+
+   Another reason to forbid the use of a single label (e.g.,
+   "fred@sales") is that many non-AAA systems treat a single label as
+   being a local identifier within their realm.  That is, a user logging
+   in as "fred@sales" to a domain "example.com" would be treated as if
+   the NAI was instead "fred@sales.example.com".  Permitting the use of
+   a single label would mean changing the interpretation and meaning of
+   a single label, which cannot be done.
+
+3.1.  Compatibility with Email Usernames
+
+   As proposed in this document, the Network Access Identifier is of the
+   form "user@realm".  Please note that while the user portion of the
+   NAI is based on the "Internet Message Format" [RFC5322] "local-part"
+   portion of an email address as extended by "Internationalized Email
+   Headers" [RFC6532], it has been modified for the purposes of
+   Section 2.2.  It does not permit quoted text along with "folding" or
+   "non-folding" whitespace that is commonly used in email addresses.
+   As such, the NAI is not necessarily equivalent to usernames used in
+   email.
+
+   However, it is a common practice to use email addresses as user
+   identifiers in AAA systems.  The ABNF in Section 2.2 is defined to be
+   close to the "addr-spec" portion of [RFC5322] as extended by
+   [RFC6532], while still being compatible with [RFC4282].
+
+   In contrast to Section 2.5 of [RFC4282], this document states that
+   the internationalization requirements for NAIs and email addresses
+   are substantially similar.  The NAI and email identifiers may be the
+   same, and both need to be entered by the user and/or the operator
+   supplying network access to that user.  There is therefore good
+   reason for the internationalization requirements to be similar.
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 19]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+3.2.  Compatibility with DNS
+
+   The "utf8-realm" portion of the NAI is intended to be compatible with
+   Internationalized Domain Names (IDNs) [RFC5890].  As defined above,
+   the "utf8-realm" portion as transported within an 8-bit clean
+   protocol such as RADIUS and EAP can contain any valid UTF-8
+   character.  There is therefore no reason for a NAS to convert the
+   "utf8-realm" portion of an NAI into Punycode encoding form [RFC3492]
+   prior to placing the NAI into a RADIUS User-Name attribute.
+
+   The NAI does not make a distinction between A-labels and U-labels, as
+   those are terms specific to DNS.  It is instead an IDNA-valid label,
+   as per the first item in Section 2.3.2.1 of [RFC5890].  As noted in
+   that section, the term "IDNA-valid label" encompasses both "A-label"
+   and "U-label".
+
+   When the realm portion of the NAI is used as the basis for name
+   resolution, it may be necessary to convert internationalized realm
+   names to Punycode [RFC3492] encoding form as described in [RFC5891].
+   As noted in Section 2 of [RFC6055], resolver Application Programming
+   Interfaces (APIs) are not necessarily DNS specific, so conversion to
+   Punycode needs to be done carefully:
+
+   Applications that convert an IDN to A-label form before calling (for
+   example) getaddrinfo() will result in name resolution failures if the
+   Punycode name is directly used in such protocols.  Having libraries
+   or protocols to convert from A-labels to the encoding scheme defined
+   by the protocol (e.g., UTF-8) would require changes to APIs and/or
+   servers, which Internationalized Domain Names for Applications (IDNA)
+   was intended to avoid.
+
+   As a result, applications SHOULD NOT assume that non-ASCII names are
+   resolvable using the public DNS and blindly convert them to A-labels
+   without knowledge of what protocol will be selected by the name
+   resolution library.
+
+3.3.  Realm Construction
+
+   The home realm usually appears in the "utf8-realm" portion of the
+   NAI, but in some cases a different realm can be used.  This may be
+   useful, for instance, when the home realm is reachable only via
+   intermediate proxies.
+
+   Such usage may prevent interoperability unless the parties involved
+   have a mutual agreement that the usage is allowed.  In particular,
+   NAIs MUST NOT use a different realm than the home realm unless the
+   sender has explicit knowledge that (a) the specified other realm is
+   available and (b) the other realm supports such usage.  The sender
+
+
+
+DeKok                        Standards Track                   [Page 20]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   may determine the fulfillment of these conditions through a database,
+   dynamic discovery, or other means not specified here.  Note that the
+   first condition is affected by roaming, as the availability of the
+   other realm may depend on the user's location or the desired
+   application.
+
+   The use of the home realm MUST be the default unless otherwise
+   configured.
+
+3.3.1.  Historical Practices
+
+   Some AAA systems have historically used NAI modifications with
+   multiple "prefix" and "suffix" decorations to perform explicit
+   routing through multiple proxies inside of a AAA network.
+
+   In RADIUS-based environments, the use of decorated NAI is NOT
+   RECOMMENDED for the following reasons:
+
+   *  Using explicit routing paths is fragile and is unresponsive to
+      changes in the network due to servers going up or down or to
+      changing business relationships.
+
+   *  There is no RADIUS routing protocol, meaning that routing paths
+      have to be communicated "out of band" to all intermediate AAA
+      nodes, and also to all edge systems (e.g., supplicants) expecting
+      to obtain network access.
+
+   *  Using explicit routing paths requires thousands, if not millions,
+      of edge systems to be updated with new path information when a AAA
+      routing path changes.  This adds huge expense for updates that
+      would be better done at only a few AAA systems in the network.
+
+   *  Manual updates to RADIUS paths are expensive, time-consuming, and
+      prone to error.
+
+   *  Creating compatible formats for the NAI is difficult when locally
+      defined "prefixes" and "suffixes" conflict with similar practices
+      elsewhere in the network.  These conflicts mean that connecting
+      two networks may be impossible in some cases, as there is no way
+      for packets to be routed properly in a way that meets all
+      requirements at all intermediate proxies.
+
+   *  Leveraging the DNS name system for realm names establishes a
+      globally unique namespace for realms.
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 21]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   In summary, network practices and capabilities have changed
+   significantly since NAIs were first overloaded to define AAA routes
+   through a network.  While manually managed explicit path routing was
+   once useful, the time has come for better methods to be used.
+
+   Notwithstanding the above recommendations, the above practice is
+   widely used for Diameter routing [RFC5729].  The routes described
+   there are managed automatically, for both credential provisioning and
+   routing updates.  Those routes also exist within a particular
+   framework (typically 3G), where membership is controlled and system
+   behavior is standardized.  There are no known issues with using
+   explicit routing in such an environment.
+
+   However, if decorated identifiers are used, such as:
+
+      homerealm.example.org!user@otherrealm.example.net
+
+   then the part before the (non-escaped) '!' MUST be a "utf8-realm" as
+   defined in the ABNF in Section 2.2.  When receiving such an
+   identifier, the "otherrealm.example.net" system MUST convert the
+   identifier to "user@homerealm.example.org" before forwarding the
+   request.  The forwarding system MUST then apply normal AAA routing
+   for the transaction, based on the updated identifier.
+
+3.4.  Examples
+
+   Examples of valid Network Access Identifiers include the following:
+
+           bob
+           joe@example.com
+           fred@foo-9.example.com
+           jack@3rd.depts.example.com
+           fred.smith@example.com
+           fred_smith@example.com
+           fred$@example.com
+           fred=?#$&*+-/^smith@example.com
+           nancy@eng.example.net
+           eng.example.net!nancy@example.net
+           eng%nancy@example.net
+           @privatecorp.example.net
+           \(user\)@example.net
+
+   An additional valid NAI is the following -- shown here as a
+   hex string, as this document can only contain ASCII characters:
+
+           626f 6240 ceb4 cebf ceba ceb9 cebc ceae 2e63 6f6d
+
+
+
+
+
+DeKok                        Standards Track                   [Page 22]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   Examples of invalid Network Access Identifiers include the following:
+
+           fred@example
+           fred@example_9.com
+           fred@example.net@example.net
+           fred.@example.net
+           eng:nancy@example.net
+           eng;nancy@example.net
+           (user)@example.net
+           <nancy>@example.net
+
+   One example given in [RFC4282] is still permitted by the ABNF, but it
+   is NOT RECOMMENDED because of the use of the Punycode [RFC3492]
+   encoding form for what is now a valid UTF-8 string:
+
+           alice@xn--tmonesimerkki-bfbb.example.net
+
+4.  Security Considerations
+
+   Since an NAI reveals the home affiliation of a user, it may assist an
+   attacker in further probing the username space.  Typically, this
+   problem is of most concern in protocols that transmit the username in
+   clear-text across the Internet, such as in RADIUS [RFC2865]
+   [RFC2866].  In order to prevent snooping of the username, protocols
+   may use confidentiality services provided by protocols transporting
+   them, such as RADIUS protected by IPsec [RFC3579] or Diameter
+   protected by TLS [RFC6733].
+
+   This specification adds the possibility of hiding the username part
+   in the NAI, by omitting it.  As discussed in Section 2.4, this is
+   possible only when NAIs are used together with a separate
+   authentication method that can transfer the username in a secure
+   manner.  In some cases, application-specific privacy mechanisms have
+   also been used with NAIs.  For instance, some EAP methods apply
+   method-specific pseudonyms in the username part of the NAI [RFC3748].
+   While neither of these approaches can protect the realm part, their
+   advantage over transport protection is that the privacy of the
+   username is protected, even through intermediate nodes such as NASes.
+
+4.1.  Correlation of Identities over Time and Protocols
+
+   The recommendations in Sections 2.7 and 2.8 for using the NAI in
+   other protocols have implications for privacy.  Any attacker who is
+   capable of observing traffic containing the NAI can track the user
+   and can correlate his activity across time and across multiple
+   protocols.  The authentication credentials therefore SHOULD be
+
+
+
+
+
+DeKok                        Standards Track                   [Page 23]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   transported over channels that permit private communications, or
+   multiple identifiers SHOULD be used, so that user tracking is
+   impossible.
+
+   It is RECOMMENDED that user privacy be enhanced by configuring
+   multiple identifiers for one user.  These identifiers can be changed
+   over time, in order to make user tracking more difficult for a
+   malicious observer.  However, provisioning and management of the
+   identifiers may be difficult to do in practice -- a likely reason why
+   multiple identifiers are rarely used today.
+
+4.2.  Multiple Identifiers
+
+   Section 1.3 states that multiple identifier formats allow attackers
+   to make contradictory claims without being detected.  This statement
+   deserves further discussion.
+
+   Section 2.4 discussed "inner" and "outer" identifiers in the context
+   of TTLS [RFC5281].  A close reading of that specification shows there
+   is no requirement that the inner and outer identifiers be in any way
+   related.  That is, it is perfectly valid to use "@example.com" for an
+   outer identifier and "user@example.org" as an inner identifier.  The
+   authentication request will then be routed to "example.com", which
+   will likely be unable to authenticate "user@example.org".
+
+   Even worse, a misconfiguration of "example.com" means that it may in
+   turn proxy the inner authentication request to the "example.org"
+   domain.  Such cross-domain authentication is highly problematic, and
+   there are few good reasons to allow it.
+
+   It is therefore RECOMMENDED that systems that permit anonymous
+   "outer" identifiers require that the "inner" domain be the same as,
+   or a subdomain of, the "outer" domain.  An authentication request
+   using disparate realms is a security violation, and the request
+   SHOULD be rejected.
+
+   The situation gets worse when multiple protocols are involved.  The
+   TTLS protocol permits Microsoft CHAP (MS-CHAP) [RFC2433] to be
+   carried inside of the TLS tunnel.  MS-CHAP defines its own
+   identifier, which is encapsulated inside of the MS-CHAP exchange.
+   That identifier is not required to be any particular format, is not
+   required to be in UTF-8, and, in practice, can be one of many unknown
+   character sets.  There is no way in practice to determine which
+   character set was used for that identifier.
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 24]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   The result is that the "outer" EAP Identity carried by TTLS is likely
+   to not even share the same character set as the "inner" identifier
+   used by MS-CHAP.  The two identifiers are entirely independent and
+   fundamentally incomparable.
+
+   Such a protocol design is NOT RECOMMENDED.
+
+5.  Administration of Names
+
+   In order to avoid creating any new administrative procedures,
+   administration of the NAI realm namespace piggybacks on the
+   administration of the DNS namespace.
+
+   NAI realm names are required to be unique, and the rights to use a
+   given NAI realm for roaming purposes are obtained coincident with
+   acquiring the rights to use a particular Fully Qualified Domain Name
+   (FQDN).  Those wishing to use an NAI realm name should first acquire
+   the rights to use the corresponding FQDN.  Administrators MUST NOT
+   publicly use an NAI realm without first owning the corresponding
+   FQDN.  Private use of unowned NAI realms within an administrative
+   domain is allowed, though it is RECOMMENDED that example names be
+   used, such as "example.com".
+
+   Note that the use of an FQDN as the realm name does not require use
+   of the DNS for location of the authentication server.  While Diameter
+   [RFC6733] supports the use of DNS for location of authentication
+   servers, existing RADIUS implementations typically use proxy
+   configuration files in order to locate authentication servers within
+   a domain and perform authentication routing.  The implementations
+   described in [RFC2194] did not use DNS for location of the
+   authentication server within a domain.  Similarly, existing
+   implementations have not found a need for dynamic routing protocols
+   or propagation of global routing information.  Note also that there
+   is no requirement that the NAI represent a valid email address.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 25]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+6.  References
+
+6.1.  Normative References
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997,
+              <http://www.rfc-editor.org/info/rfc2119>.
+
+   [RFC3629]  Yergeau, F., "UTF-8, a transformation format of
+              ISO 10646", STD 63, RFC 3629, November 2003,
+              <http://www.rfc-editor.org/info/rfc3629>.
+
+   [RFC5198]  Klensin, J. and M. Padlipsky, "Unicode Format for Network
+              Interchange", RFC 5198, March 2008,
+              <http://www.rfc-editor.org/info/rfc5198>.
+
+   [RFC5234]  Crocker, D., Ed., and P. Overell, "Augmented BNF for
+              Syntax Specifications: ABNF", STD 68, RFC 5234,
+              January 2008, <http://www.rfc-editor.org/info/rfc5234>.
+
+   [RFC5890]  Klensin, J., "Internationalized Domain Names for
+              Applications (IDNA): Definitions and Document Framework",
+              RFC 5890, August 2010,
+              <http://www.rfc-editor.org/info/rfc5890>.
+
+   [RFC5891]  Klensin, J., "Internationalized Domain Names in
+              Applications (IDNA): Protocol", RFC 5891, August 2010,
+              <http://www.rfc-editor.org/info/rfc5891>.
+
+   [RFC6365]  Hoffman, P. and J. Klensin, "Terminology Used in
+              Internationalization in the IETF", BCP 166, RFC 6365,
+              September 2011, <http://www.rfc-editor.org/info/rfc6365>.
+
+6.2.  Informative References
+
+   [RFC2194]  Aboba, B., Lu, J., Alsop, J., Ding, J., and W. Wang,
+              "Review of Roaming Implementations", RFC 2194,
+              September 1997, <http://www.rfc-editor.org/info/rfc2194>.
+
+   [RFC2341]  Valencia, A., Littlewood, M., and T. Kolar, "Cisco
+              Layer Two Forwarding (Protocol) "L2F"", RFC 2341,
+              May 1998, <http://www.rfc-editor.org/info/rfc2341>.
+
+   [RFC2433]  Zorn, G. and S. Cobb, "Microsoft PPP CHAP Extensions",
+              RFC 2433, October 1998,
+              <http://www.rfc-editor.org/info/rfc2433>.
+
+
+
+
+
+DeKok                        Standards Track                   [Page 26]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   [RFC2637]  Hamzeh, K., Pall, G., Verthein, W., Taarud, J., Little,
+              W., and G. Zorn, "Point-to-Point Tunneling Protocol
+              (PPTP)", RFC 2637, July 1999,
+              <http://www.rfc-editor.org/info/rfc2637>.
+
+   [RFC2661]  Townsley, W., Valencia, A., Rubens, A., Pall, G., Zorn,
+              G., and B. Palter, "Layer Two Tunneling Protocol "L2TP"",
+              RFC 2661, August 1999,
+              <http://www.rfc-editor.org/info/rfc2661>.
+
+   [RFC2865]  Rigney, C., Willens, S., Rubens, A., and W. Simpson,
+              "Remote Authentication Dial In User Service (RADIUS)",
+              RFC 2865, June 2000,
+              <http://www.rfc-editor.org/info/rfc2865>.
+
+   [RFC2866]  Rigney, C., "RADIUS Accounting", RFC 2866, June 2000,
+              <http://www.rfc-editor.org/info/rfc2866>.
+
+   [RFC3492]  Costello, A., "Punycode: A Bootstring encoding of Unicode
+              for Internationalized Domain Names in Applications
+              (IDNA)", RFC 3492, March 2003,
+              <http://www.rfc-editor.org/info/rfc3492>.
+
+   [RFC3579]  Aboba, B. and P. Calhoun, "RADIUS (Remote Authentication
+              Dial In User Service) Support For Extensible
+              Authentication Protocol (EAP)", RFC 3579, September 2003,
+              <http://www.rfc-editor.org/info/rfc3579>.
+
+   [RFC3748]  Aboba, B., Blunk, L., Vollbrecht, J., Carlson, J., and H.
+              Levkowetz, Ed., "Extensible Authentication Protocol
+              (EAP)", RFC 3748, June 2004,
+              <http://www.rfc-editor.org/info/rfc3748>.
+
+   [RFC4282]  Aboba, B., Beadles, M., Arkko, J., and P. Eronen, "The
+              Network Access Identifier", RFC 4282, December 2005,
+              <http://www.rfc-editor.org/info/rfc4282>.
+
+   [RFC4301]  Kent, S. and K. Seo, "Security Architecture for the
+              Internet Protocol", RFC 4301, December 2005,
+              <http://www.rfc-editor.org/info/rfc4301>.
+
+   [RFC5281]  Funk, P. and S. Blake-Wilson, "Extensible Authentication
+              Protocol Tunneled Transport Layer Security Authenticated
+              Protocol Version 0 (EAP-TTLSv0)", RFC 5281, August 2008,
+              <http://www.rfc-editor.org/info/rfc5281>.
+
+   [RFC5322]  Resnick, P., Ed., "Internet Message Format", RFC 5322,
+              October 2008, <http://www.rfc-editor.org/info/rfc5322>.
+
+
+
+DeKok                        Standards Track                   [Page 27]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   [RFC5335]  Yang, A., Ed., "Internationalized Email Headers",
+              RFC 5335, September 2008,
+              <http://www.rfc-editor.org/info/rfc5335>.
+
+   [RFC5729]  Korhonen, J., Ed., Jones, M., Morand, L., and T. Tsou,
+              "Clarifications on the Routing of Diameter Requests Based
+              on the Username and the Realm", RFC 5729, December 2009,
+              <http://www.rfc-editor.org/info/rfc5729>.
+
+   [RFC6055]  Thaler, D., Klensin, J., and S. Cheshire, "IAB Thoughts on
+              Encodings for Internationalized Domain Names", RFC 6055,
+              February 2011, <http://www.rfc-editor.org/info/rfc6055>.
+
+   [RFC6532]  Yang, A., Steele, S., and N. Freed, "Internationalized
+              Email Headers", RFC 6532, February 2012,
+              <http://www.rfc-editor.org/info/rfc6532>.
+
+   [RFC6733]  Fajardo, V., Ed., Arkko, J., Loughney, J., and G. Zorn,
+              Ed., "Diameter Base Protocol", RFC 6733, October 2012,
+              <http://www.rfc-editor.org/info/rfc6733>.
+
+   [RFC6912]  Sullivan, A., Thaler, D., Klensin, J., and O. Kolkman,
+              "Principles for Unicode Code Point Inclusion in Labels in
+              the DNS", RFC 6912, April 2013,
+              <http://www.rfc-editor.org/info/rfc6912>.
+
+   [EDUROAM]  "eduroam (EDUcation ROAMing)", <http://eduroam.org>.
+
+   [3GPP]     3GPP, "Numbering, addressing and Identification", 3GPP TS
+              23.003, Release 12, July 2014,
+              <ftp://ftp.3gpp.org/Specs/archive/23_series/23.003/>.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 28]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+Appendix A.  Changes from RFC 4282
+
+   This document contains the following updates with respect to the
+   previous NAI definition in RFC 4282 [RFC4282]:
+
+   *  The formal syntax in Section 2.1 has been updated to forbid
+      non-UTF-8 characters (e.g., characters with the "high bit" set).
+
+   *  The formal syntax in Section 2.1 of [RFC4282] has been updated to
+      allow UTF-8 in the "realm" portion of the NAI.
+
+   *  The formal syntax in Section 2.1 of [RFC4282] applied to the NAI
+      after it was "internationalized" via the ToAscii function.  The
+      contents of the NAI before it was "internationalized" were left
+      indeterminate.  This document updates the formal syntax to define
+      an internationalized form of the NAI and forbids the use of the
+      ToAscii function for NAI "internationalization".
+
+   *  The grammar for the user and realm portion is based on a
+      combination of the "nai" defined in Section 2.1 of [RFC4282] and
+      the "utf8-addr-spec" defined in Section 4.4 of [RFC5335].
+
+   *  All use of the ToAscii function has been moved to normal
+      requirements on DNS implementations when realms are used as the
+      basis for DNS lookups.  This involves no changes to the existing
+      DNS infrastructure.
+
+   *  The discussions on internationalized character sets in Section 2.4
+      of [RFC4282] have been updated.  The suggestion to use the ToAscii
+      function for realm comparisons has been removed.  No AAA system
+      has implemented these suggestions, so this change should have no
+      operational impact.
+
+   *  The "Routing inside of AAA Systems" section is new in this
+      document.  The concept of a "local AAA routing table" is also new,
+      although it accurately describes the functionality of widespread
+      implementations.
+
+   *  The "Compatibility with EMail Usernames" and "Compatibility with
+      DNS" sections have been revised and updated.  The Punycode
+      transformation is suggested to be used only when a realm name is
+      used for DNS lookups, and even then the function is only used by a
+      resolving API on the local system, and even then it is recommended
+      that only the home network perform this conversion.
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 29]
+\f
+RFC 7542              The Network Access Identifier             May 2015
+
+
+   *  The "Realm Construction" section has been updated to note that
+      editing of the NAI is NOT RECOMMENDED.
+
+   *  The "Examples" section has been updated to remove the instance of
+      the IDN being converted to ASCII.  This behavior is now forbidden.
+
+Acknowledgments
+
+   The initial text for this document was [RFC4282], which was then
+   heavily edited.  The original authors of [RFC4282] were Bernard
+   Aboba, Mark A. Beadles, Jari Arkko, and Pasi Eronen.
+
+Author's Address
+
+   Alan DeKok
+   The FreeRADIUS Server Project
+
+   EMail: aland@freeradius.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+DeKok                        Standards Track                   [Page 30]
+\f
diff --git a/doc/rfc/rfc7599.txt b/doc/rfc/rfc7599.txt
new file mode 100644 (file)
index 0000000..ac0cc30
--- /dev/null
@@ -0,0 +1,1515 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF)                             X. Li
+Request for Comments: 7599                                        C. Bao
+Category: Standards Track                            Tsinghua University
+ISSN: 2070-1721                                              W. Dec, Ed.
+                                                                O. Troan
+                                                           Cisco Systems
+                                                           S. Matsushima
+                                                        SoftBank Telecom
+                                                             T. Murakami
+                                                             IP Infusion
+                                                               July 2015
+
+
+         Mapping of Address and Port using Translation (MAP-T)
+
+Abstract
+
+   This document specifies the solution architecture based on "Mapping
+   of Address and Port" stateless IPv6-IPv4 Network Address Translation
+   (NAT64) for providing shared or non-shared IPv4 address connectivity
+   to and across an IPv6 network.
+
+Status of This Memo
+
+   This is an Internet Standards Track document.
+
+   This document is a product of the Internet Engineering Task Force
+   (IETF).  It represents the consensus of the IETF community.  It has
+   received public review and has been approved for publication by the
+   Internet Engineering Steering Group (IESG).  Further information on
+   Internet Standards is available in Section 2 of RFC 5741.
+
+   Information about the current status of this document, any errata,
+   and how to provide feedback on it may be obtained at
+   http://www.rfc-editor.org/info/rfc7599.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                    [Page 1]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+Copyright Notice
+
+   Copyright (c) 2015 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                    [Page 2]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+Table of Contents
+
+   1. Introduction ....................................................4
+   2. Conventions .....................................................4
+   3. Terminology .....................................................5
+   4. Architecture ....................................................6
+   5. Mapping Rules ...................................................8
+      5.1. Destinations outside the MAP Domain ........................8
+   6. The IPv6 Interface Identifier ...................................9
+   7. MAP-T Configuration ............................................10
+      7.1. MAP CE ....................................................10
+      7.2. MAP BR ....................................................11
+   8. MAP-T Packet Forwarding ........................................11
+      8.1. IPv4 to IPv6 at the CE ....................................11
+      8.2. IPv6 to IPv4 at the CE ....................................12
+      8.3. IPv6 to IPv4 at the BR ....................................12
+      8.4. IPv4 to IPv6 at the BR ....................................13
+   9. ICMP Handling ..................................................13
+   10. Fragmentation and Path MTU Discovery ..........................14
+      10.1. Fragmentation in the MAP Domain ..........................14
+      10.2. Receiving IPv4 Fragments on the MAP Domain Borders .......14
+      10.3. Sending IPv4 Fragments to the Outside ....................14
+   11. NAT44 Considerations ..........................................15
+   12. Usage Considerations ..........................................15
+      12.1. EA-Bit Length 0 ..........................................15
+      12.2. Mesh and Hub-and-Spoke Modes .............................15
+      12.3. Communication with IPv6 Servers in the MAP-T Domain ......15
+      12.4. Compatibility with Other NAT64 Solutions .................16
+   13. Security Considerations .......................................16
+   14. References ....................................................17
+      14.1. Normative References .....................................17
+      14.2. Informative References ...................................18
+   Appendix A. Examples of MAP-T Translation .........................21
+   Appendix B. Port-Mapping Algorithm ................................24
+   Acknowledgements ..................................................25
+   Contributors ......................................................25
+   Authors' Addresses ................................................26
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                    [Page 3]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+1.  Introduction
+
+   Experiences from initial service provider IPv6 network deployments,
+   such as [RFC6219], indicate that successful transition to IPv6 can
+   happen while supporting legacy IPv4 users without a full end-to-end
+   dual-IP-stack deployment.  However, due to public IPv4 address
+   exhaustion, this requires an IPv6 technology that supports IPv4 users
+   utilizing shared IPv4 addressing, while also allowing the network
+   operator to optimize their operations around IPv6 network practices.
+   The use of double NAT64 translation-based solutions is an optimal way
+   to address these requirements, especially in combination with
+   stateless translation techniques that minimize operational challenges
+   outlined in [Solutions-4v6].
+
+   The Mapping of Address and Port using Translation (MAP-T)
+   architecture specified in this document is such a double stateless
+   NAT64-based solution.  It builds on existing stateless NAT64
+   techniques specified in [RFC6145], along with the stateless
+   algorithmic address and transport-layer port-mapping scheme defined
+   in the Mapping of Address and Port with Encapsulation (MAP-E)
+   specification [RFC7597].  The MAP-T solution differs from MAP-E in
+   that MAP-T uses IPv4-IPv6 translation, rather than encapsulation, as
+   the form of IPv6 domain transport.  The translation mode is
+   considered advantageous in scenarios where the encapsulation
+   overhead, or IPv6 operational practices (e.g., the use of IPv6-only
+   servers, or reliance on IPv6 + protocol headers for traffic
+   classification) rule out encapsulation.  These scenarios are
+   presented in [MAP-T-Use-Cases].
+
+2.  Conventions
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in RFC 2119 [RFC2119].
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                    [Page 4]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+3.  Terminology
+
+   MAP-T:                  Mapping of Address and Port using
+                           Translation.
+
+   MAP Customer Edge (CE): A device functioning as a Customer Edge
+                           router in a MAP deployment.  A typical MAP CE
+                           adopting MAP Rules will serve a residential
+                           site with one WAN-side IPv6-addressed
+                           interface and one or more LAN-side interfaces
+                           addressed using private IPv4 addressing.
+
+   MAP Border Relay (BR):  A MAP-enabled router managed by the service
+                           provider at the edge of a MAP domain.  A BR
+                           has at least an IPv6-enabled interface and an
+                           IPv4 interface connected to the native IPv4
+                           network.  A MAP BR may also be referred to as
+                           simply a "BR" within the context of MAP.
+
+   MAP domain:             One or more MAP CEs and BRs connected by
+                           means of an IPv6 network and sharing a common
+                           set of MAP Rules.  A service provider may
+                           deploy a single MAP domain or may utilize
+                           multiple MAP domains.
+
+   MAP Rule:               A set of parameters describing the mapping
+                           between an IPv4 prefix, IPv4 address, or
+                           shared IPv4 address and an IPv6 prefix or
+                           address.  Each MAP domain uses a different
+                           mapping rule set.
+
+   MAP rule set:           A rule set is composed of all the MAP Rules
+                           communicated to a device that are intended to
+                           determine the device's IP+port mapping and
+                           forwarding operations.  The MAP rule set is
+                           interchangeably referred to in this document
+                           as a MAP rule table or as simply a "rule
+                           table".  Two specific types of rules -- the
+                           Basic Mapping Rule (BMR) and the Forwarding
+                           Mapping Rule (FMR) -- are defined in
+                           Section 5 of [RFC7597].  The Default Mapping
+                           Rule (DMR) is defined in this document.
+
+   MAP rule table:         See MAP rule set.
+
+   MAP node:               A device that implements MAP.
+
+
+
+
+
+Li, et al.                   Standards Track                    [Page 5]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   Port set:               Each node has a separate part of the
+                           transport-layer port space; this is denoted
+                           as a port set.
+
+   Port Set ID (PSID):     Algorithmically identifies a set of ports
+                           exclusively assigned to a CE.
+
+   Shared IPv4 address:    An IPv4 address that is shared among multiple
+                           CEs.  Only ports that belong to the assigned
+                           port set can be used for communication.  Also
+                           known as a port-restricted IPv4 address.
+
+   End-user IPv6 prefix:   The IPv6 prefix assigned to an End-user CE by
+                           means other than MAP itself, e.g.,
+                           provisioned using DHCPv6 Prefix Delegation
+                           (PD) [RFC3633], assigned via Stateless
+                           Address Autoconfiguration (SLAAC) [RFC4862],
+                           or configured manually.  It is unique for
+                           each CE.
+
+   MAP IPv6 address:       The IPv6 address used to reach the MAP
+                           function of a CE from other CEs and from BRs.
+
+   Rule IPv6 prefix:       An IPv6 prefix assigned by a service provider
+                           for a MAP Rule.
+
+   Rule IPv4 prefix:       An IPv4 prefix assigned by a service provider
+                           for a MAP Rule.
+
+   Embedded Address (EA) bits:
+                           The IPv4 EA-bits in the IPv6 address identify
+                           an IPv4 prefix/address (or part thereof) or a
+                           shared IPv4 address (or part thereof) and a
+                           Port Set Identifier.
+
+4.  Architecture
+
+   Figure 1 depicts the overall MAP-T architecture, which sees any
+   number of privately addressed IPv4 users (N and M) connected by means
+   of MAP-T CEs to an IPv6 network that is equipped with one or more
+   MAP-T BRs.  CEs and BRs that share MAP configuration parameters,
+   referred to as "MAP Rules", form a MAP-T domain.
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                    [Page 6]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   Functionally, the MAP-T CE and BR utilize and extend some
+   well-established technology building blocks to allow the IPv4 users
+   to correspond with nodes on the public IPv4 network or on the IPv6
+   network as follows:
+
+   o  A (NAT44) Network Address and Port Translation (NAPT) [RFC2663]
+      function on a MAP CE is extended with support for restricting the
+      allowable TCP/UDP ports for a given IPv4 address.  The IPv4
+      address and port range used are determined by the MAP provisioning
+      process and identical to MAP-E [RFC7597].
+
+   o  A stateless NAT64 function [RFC6145] is extended to allow
+      stateless mapping of IPv4 and transport-layer port ranges to the
+      IPv6 address space.
+
+         User N
+       Private IPv4
+      |  Network
+      |
+   O--+---------------O
+   |  | MAP-T CE      |
+   | +-----+--------+ |
+   | NAPT44|  MAP-T | |
+   | +-----+        | +-._   ,-------.                     .------.
+   |       +--------+ |   ,-'         `-.                ,-'       `-.
+   O------------------O  /              \   O---------O /   Public   \
+                         /   IPv6-only   \  |  MAP-T  |/     IPv4     \
+                        (    Network      --+  Border +-   Network     )
+                         \               /  |  Relay  |\              /
+   O------------------O  \              /   O---------O \             /
+   |    MAP-T CE      |   ;".         ,-'                `-.       ,-'
+   | +-----+--------+ | ,"   `----+--'                      ------'
+   | NAPT44|  MAP-T | |,          |
+   | +-----+        | +        IPv6 node(s)
+   |   |   +--------+ |  (with IPv4-embedded IPv6 address)
+   O---+--------------O
+       |
+         User M
+       Private IPv4
+         Network
+
+                       Figure 1: MAP-T Architecture
+
+   Each MAP-T CE is assigned with a regular IPv6 prefix from the
+   operator's IPv6 network.  This, in conjunction with MAP domain
+   configuration settings and the use of the MAP procedures, allows the
+   computation of a MAP IPv6 address and a corresponding IPv4 address.
+   To allow for IPv4 address sharing, the CE may also have to be
+
+
+
+Li, et al.                   Standards Track                    [Page 7]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   configured with a TCP/UDP port range that is identified by means of a
+   MAP Port Set Identifier (PSID) value.  Each CE is responsible for
+   forwarding traffic between a given user's private IPv4 address space
+   and the MAP domain's IPv6 address space.  The IPv4-IPv6 adaptation
+   uses stateless NAT64, in conjunction with the MAP algorithm for
+   address computation.
+
+   The MAP-T BR connects one or more MAP-T domains to external IPv4
+   networks using stateless NAT64 as extended by the MAP-T behavior
+   described in this document.
+
+   In contrast to MAP-E, NAT64 technology is used in the architecture
+   for two purposes.  First, it is intended to diminish encapsulation
+   overhead and allow IPv4 and IPv6 traffic to be treated as similarly
+   as possible.  Second, it is intended to allow IPv4-only nodes to
+   correspond directly with IPv6 nodes in the MAP-T domain that have
+   IPv4-embedded IPv6 addresses as per [RFC6052].
+
+   The MAP-T architecture is based on the following key properties:
+
+   1.  Algorithmic IPv4-IPv6 address mapping codified as MAP Rules, as
+       described in Section 5
+
+   2.  A MAP IPv6 address identifier, as described in Section 6
+
+   3.  MAP-T IPv4-IPv6 forwarding behavior, as described in Section 8
+
+5.  Mapping Rules
+
+   The MAP-T algorithmic mapping rules are identical to those in
+   Section 5 of the MAP-E specification [RFC7597], with the following
+   exception: the forwarding of traffic to and from IPv4 destinations
+   outside a MAP-T domain is to be performed as described in this
+   document, instead of Section 5.4 of the MAP-E specification.
+
+5.1.  Destinations outside the MAP Domain
+
+   IPv4 traffic sent by MAP nodes that are all within one MAP domain is
+   translated to IPv6, with the sender's MAP IPv6 address, derived via
+   the Basic Mapping Rule (BMR), as the IPv6 source address and the
+   recipient's MAP IPv6 address, derived via the Forwarding Mapping Rule
+   (FMR), as the IPv6 destination address.
+
+   IPv4-addressed destinations outside of the MAP domain are represented
+   by means of IPv4-embedded IPv6 addresses as per [RFC6052], using the
+   BR's IPv6 prefix.  For a CE sending traffic to any such destination,
+   the source address of the IPv6 packet will be that of the CE's MAP
+   IPv6 address, and the destination IPv6 address will be the
+
+
+
+Li, et al.                   Standards Track                    [Page 8]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   destination IPv4-embedded IPv6 address.  This address mapping is said
+   to be following the MAP-T Default Mapping Rule (DMR) and is defined
+   in terms of the IPv6 prefix advertised by one or more BRs, which
+   provide external connectivity.  A typical MAP-T CE will install an
+   IPv4 default route using this rule.  A BR will use this rule when
+   translating all outside IPv4 source addresses to the IPv6 MAP domain.
+
+   The DMR IPv6 prefix length SHOULD be 64 bits long by default and in
+   any case MUST NOT exceed 96 bits.  The mapping of the IPv4
+   destination behind the IPv6 prefix will by default follow the /64
+   rule as per [RFC6052].  Any trailing bits after the IPv4 address are
+   set to 0x0.
+
+6.  The IPv6 Interface Identifier
+
+   The interface identifier format of a MAP-T node is the same as the
+   format described in Section 6 of [RFC7597].  The format diagram is
+   provided here for convenience:
+
+                   |          128-n-o-s bits          |
+                   | 16 bits|    32 bits     | 16 bits|
+                   +--------+----------------+--------+
+                   |   0    |  IPv4 address  |  PSID  |
+                   +--------+----------------+--------+
+
+                    Figure 2: IPv6 Interface Identifier
+
+   In the case of an IPv4 prefix, the IPv4 address field is right-padded
+   with zeros up to 32 bits.  The PSID is left-padded with zeros to
+   create a 16-bit field.  For an IPv4 prefix or a complete IPv4
+   address, the PSID field is zero.
+
+   If the End-user IPv6 prefix length is larger than 64, the most
+   significant parts of the interface identifier are overwritten by the
+   prefix.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                    [Page 9]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+7.  MAP-T Configuration
+
+   For a given MAP domain, the BR and CE MUST be configured with the
+   following MAP parameters.  The values for these parameters are
+   identical for all CEs and BRs within a given MAP-T domain.
+
+   o  The Basic Mapping Rule and, optionally, the Forwarding Mapping
+      Rules, including the Rule IPv6 prefix, Rule IPv4 prefix, and
+      Length of embedded address bits
+
+   o  Use of hub-and-spoke mode or Mesh mode (if all traffic should be
+      sent to the BR, or if direct CE-to-CE correspondence should be
+      supported)
+
+   o  Use of IPv4-IPv6 translation (MAP-T)
+
+   o  The BR's IPv6 prefix used in the DMR
+
+7.1.  MAP CE
+
+   For a given MAP domain, the MAP configuration parameters are the same
+   across all CEs within that domain.  These values may be conveyed and
+   configured on the CEs using a variety of methods, including DHCPv6,
+   the Broadband Forum's "TR-69" Residential Gateway management
+   interface [TR069], the Network Configuration Protocol (NETCONF), or
+   manual configuration.  This document does not prescribe any of these
+   methods but recommends that a MAP CE SHOULD implement DHCPv6 options
+   as per [RFC7598].  Other configuration and management methods may use
+   the data model described by this option for consistency and
+   convenience of implementation on CEs that support multiple
+   configuration methods.
+
+   Besides the MAP configuration parameters, a CE requires an IPv6
+   prefix to be assigned to the CE.  This End-user IPv6 prefix is
+   configured as part of obtaining IPv6 Internet access and is acquired
+   using standard IPv6 means applicable in the network where the CE is
+   located.
+
+   The MAP provisioning parameters, and hence the IPv4 service itself,
+   are tied to the End-user IPv6 prefix; thus, the MAP service is also
+   tied to this in terms of authorization, accounting, etc.
+
+   A single MAP CE MAY be connected to more than one MAP domain, just as
+   any router may have more than one IPv4-enabled service-provider-
+   facing interface and more than one set of associated addresses
+   assigned by DHCPv6.  Each domain within which a given CE operates
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 10]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   would require its own set of MAP configuration elements and would
+   generate its own IPv4 address.  Each MAP domain requires a distinct
+   End-user IPv6 prefix.
+
+7.2.  MAP BR
+
+   The MAP BR MUST be configured with the same MAP elements as the MAP
+   CEs operating within the same domain.
+
+   For increased reliability and load balancing, the BR IPv6 prefix MAY
+   be shared across a given MAP domain.  As MAP is stateless, any BR may
+   be used for forwarding to/from the domain at any time.
+
+   Since MAP uses provider address space, no specific IPv6 or IPv4
+   routes need to be advertised externally outside the service
+   provider's network for MAP to operate.  However, the BR prefix needs
+   to be advertised in the service provider's IGP.
+
+8.  MAP-T Packet Forwarding
+
+   The end-to-end packet flow in MAP-T involves an IPv4 or IPv6 packet
+   being forwarded by a CE or BR in one of two directions for each such
+   case.  This section presents a conceptual view of the operations
+   involved in such forwarding.
+
+8.1.  IPv4 to IPv6 at the CE
+
+   A MAP-T CE receiving IPv4 packets SHOULD perform NAPT44 processing
+   and create any necessary NAPT44 bindings.  The source address and
+   source port range of packets resulting from the NAPT44 processing
+   MUST correspond to the source IPv4 address and source transport port
+   range assigned to the CE by means of the MAP Basic Mapping Rule
+   (BMR).
+
+   The IPv4 packet is subject to a longest IPv4 destination address +
+   port match MAP Rule selection, which then determines the parameters
+   for the subsequent NAT64 operation.  By default, all traffic is
+   matched to the DMR and is subject to the stateless NAT64 operation
+   using the DMR parameters for NAT64 (Section 5.1).  Packets that are
+   matched to (optional) Forwarding Mapping Rules (FMRs) are subject to
+   the stateless NAT64 operation using the FMR parameters (Section 5)
+   for the MAP algorithm.  In all cases, the CE's MAP IPv6 address
+   (Section 6) is used as a source address.
+
+   A MAP-T CE MUST support a Default Mapping Rule and SHOULD support one
+   or more Forwarding Mapping Rules.
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 11]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+8.2.  IPv6 to IPv4 at the CE
+
+   A MAP-T CE receiving an IPv6 packet performs its regular IPv6
+   operations (filtering, pre-routing, etc.).  Only packets that are
+   addressed to the CE's MAP-T IPv6 addresses, and with source addresses
+   matching the IPv6 MAP Rule prefixes of a DMR or FMR, are processed by
+   the MAP-T CE, with the DMR or FMR being selected based on a longest
+   match.  The CE MUST check that each MAP-T received packet's
+   transport-layer destination port number is in the range allowed for
+   by the CE's MAP BMR configuration.  The CE MUST silently drop any
+   nonconforming packet and increment an appropriate counter.  When
+   receiving a packet whose source IP address longest matches an FMR
+   prefix, the CE MUST perform a check of consistency of the source
+   address against the allowed values as per the derived allocated
+   source port range.  If the source port number of a packet is found to
+   be outside the allocated range, the CE MUST drop the packet and
+   SHOULD respond with an ICMPv6 "Destination Unreachable, source
+   address failed ingress/egress policy" (Type 1, Code 5).
+
+   For each MAP-T processed packet, the CE's NAT64 function MUST compute
+   an IPv4 source and destination address.  The IPv4 destination address
+   is computed by extracting relevant information from the IPv6
+   destination and the information stored in the BMR as per Section 5.
+   The IPv4 source address is formed by classifying a packet's source as
+   longest matching a DMR or FMR rule prefix, and then using the
+   respective rule parameters for the NAT64 operation.
+
+   The resulting IPv4 packet is then forwarded to the CE's NAPT44
+   function, where the destination IPv4 address and port number MUST be
+   mapped to their original value before being forwarded according to
+   the CE's regular IPv4 rules.  When the NAPT44 function is not
+   enabled, by virtue of MAP configuration, the traffic from the
+   stateless NAT64 function is directly forwarded according to the CE's
+   IPv4 rules.
+
+8.3.  IPv6 to IPv4 at the BR
+
+   A MAP-T BR receiving an IPv6 packet MUST select a matching MAP Rule
+   based on a longest address match of the packet's source address
+   against the MAP Rules present on the BR.  In combination with the
+   Port Set ID derived from the packet's source IPv6 address, the
+   selected MAP Rule allows the BR to verify that the CE is using its
+   allowed address and port range.  Thus, the BR MUST perform a
+   validation of the consistency of the source against the allowed
+   values from the identified port range.  If the packet's source port
+   number is found to be outside the range allowed, the BR MUST drop the
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 12]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   packet and increment a counter to indicate the event.  The BR SHOULD
+   also respond with an ICMPv6 "Destination Unreachable, source address
+   failed ingress/egress policy" (Type 1, Code 5).
+
+   When constructing the IPv4 packet, the BR MUST derive the source and
+   destination IPv4 addresses as per Section 5 of this document and
+   translate the IPv6-to-IPv4 headers as per [RFC6145].  The resulting
+   IPv4 packet is then passed to regular IPv4 forwarding.
+
+8.4.  IPv4 to IPv6 at the BR
+
+   A MAP-T BR receiving IPv4 packets uses a longest match IPv4 +
+   transport-layer port lookup to identify the target MAP-T domain and
+   select the FMR and DMR rules.  The MAP-T BR MUST then compute and
+   apply the IPv6 destination addresses from the IPv4 destination
+   address and port as per the selected FMR.  The MAP-T BR MUST also
+   compute and apply the IPv6 source addresses from the IPv4 source
+   address as per Section 5.1 (i.e., using the IPv4 source and the BR's
+   IPv6 prefix, it forms an IPv6-embedded IPv4 address).  The generic
+   IPv4-to-IPv6 header translation procedures outlined in [RFC6145]
+   apply throughout.  The resulting IPv6 packets are then passed to
+   regular IPv6 forwarding.
+
+   Note that the operation of a BR, when forwarding to/from MAP-T
+   domains that are defined without IPv4 address sharing, is the same as
+   that of stateless NAT64 IPv4/IPv6 translation.
+
+9.  ICMP Handling
+
+   MAP-T CEs and BRs MUST follow ICMP/ICMPv6 translation as per
+   [RFC6145]; however, additional behavior is also required due to the
+   presence of NAPT44.  Unlike TCP and UDP, which provide two transport-
+   protocol port fields to represent both source and destination, the
+   ICMP/ICMPv6 [RFC792] [RFC4443] Query message header has only one ID
+   field, which needs to be used to identify a sending IPv4 host.  When
+   receiving IPv4 ICMP messages, the MAP-T CE MUST rewrite the ID field
+   to a port value derived from the CE's Port Set ID.
+
+   A MAP-T BR receiving an IPv4 ICMP packet that contains an ID field
+   that is bound for a shared address in the MAP-T domain SHOULD use the
+   ID value as a substitute for the destination port in determining the
+   IPv6 destination address.  In all other cases, the MAP-T BR MUST
+   derive the destination IPv6 address by simply mapping the destination
+   IPv4 address without additional port information.
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 13]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+10.  Fragmentation and Path MTU Discovery
+
+   Due to the different sizes of the IPv4 and IPv6 headers, handling the
+   maximum packet size is relevant for the operation of any system
+   connecting the two address families.  There are three mechanisms to
+   handle this issue: Path MTU Discovery (PMTUD), fragmentation, and
+   transport-layer negotiation such as the TCP Maximum Segment Size
+   (MSS) option [RFC879].  MAP can use all three mechanisms to deal with
+   different cases.
+
+   Note: The NAT64 [RFC6145] mechanism is not lossless.  When
+   IPv4-originated communication traverses a double NAT64 function
+   (a.k.a. NAT464), any IPv4-originated ICMP-independent Path MTU
+   Discovery, as specified in [RFC4821], ceases to be entirely reliable.
+   This is because the DF=1/MF=1 combination as defined in [RFC4821]
+   results in DF=0/MF=1 after a double NAT64 translation.
+
+10.1.  Fragmentation in the MAP Domain
+
+   Translating an IPv4 packet to carry it across the MAP domain will
+   increase its size (typically by 20 bytes).  The MTU in the MAP domain
+   should be well managed, and the IPv6 MTU on the CE WAN-side interface
+   SHOULD be configured so that no fragmentation occurs within the
+   boundary of the MAP domain.
+
+   Fragmentation in MAP-T domains SHOULD be handled as described in
+   Sections 4 and 5 of [RFC6145].
+
+10.2.  Receiving IPv4 Fragments on the MAP Domain Borders
+
+   The forwarding of an IPv4 packet received from outside of the MAP
+   domain requires the IPv4 destination address and the transport-
+   protocol destination port.  The transport-protocol information is
+   only available in the first fragment received.  As described in
+   Section 5.3.3 of [RFC6346], a MAP node receiving an IPv4 fragmented
+   packet from outside SHOULD reassemble the packet before sending the
+   packet onto the MAP domain.  If the first packet received contains
+   the transport-protocol information, it is possible to optimize this
+   behavior by using a cache and forwarding the fragments unchanged.  A
+   description of such a caching algorithm is outside the scope of this
+   document.
+
+10.3.  Sending IPv4 Fragments to the Outside
+
+   Two IPv4 hosts behind two different MAP CEs with the same IPv4
+   address sending fragments to an IPv4 destination host outside the
+   domain may happen to use the same IPv4 fragmentation identifier,
+   resulting in incorrect reassembly of the fragments at the destination
+
+
+
+Li, et al.                   Standards Track                   [Page 14]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   host.  Given that the IPv4 fragmentation identifier is a 16-bit
+   field, it can be used similarly to port ranges.  Thus, a MAP CE
+   SHOULD rewrite the IPv4 fragmentation identifier to a value
+   equivalent to a port of its allocated port set.
+
+11.  NAT44 Considerations
+
+   The NAT44 implemented in the MAP CE SHOULD conform to the behavior
+   and best current practices documented in [RFC4787], [RFC5508], and
+   [RFC5382].  In MAP address-sharing mode (determined by the MAP
+   domain / rule configuration parameters), the operation of the NAT44
+   MUST be restricted to the available port numbers derived via the
+   Basic Mapping Rule.
+
+12.  Usage Considerations
+
+12.1.  EA-Bit Length 0
+
+   The MAP solution supports the use and configuration of domains where
+   a BMR expresses an EA-bit length of 0.  This results in independence
+   between the IPv6 prefix assigned to the CE and the IPv4 address
+   and/or port range used by MAP.  The k-bits of PSID information may in
+   this case be derived from the BMR.
+
+   The constraint imposed is that each such MAP domain be composed of
+   just one MAP CE that has a predetermined IPv6 end-user prefix.  The
+   BR would be configured with an FMR for each such Customer Premises
+   Equipment (CPE), where the rule would uniquely associate the IPv4
+   address + optional PSID and the IPv6 prefix of that given CE.
+
+12.2.  Mesh and Hub-and-Spoke Modes
+
+   The hub-and-spoke mode of communication, whereby all traffic sent by
+   a MAP-T CE is forwarded via a BR, and the Mesh mode, whereby a CE is
+   directly able to forward traffic to another CE, are governed by the
+   activation of Forwarding Mapping Rules that cover the IPv4-prefix
+   destination and port-index range.  By default, a MAP CE configured
+   only with a BMR, as per this specification, will use it to configure
+   its IPv4 parameters and IPv6 MAP address without enabling Mesh mode.
+
+12.3.  Communication with IPv6 Servers in the MAP-T Domain
+
+   By default, MAP-T allows communication between both IPv4-only and any
+   IPv6-enabled devices, as well as with native IPv6-only servers,
+   provided that the servers are configured with an IPv4-mapped IPv6
+   address.  This address could be part of the IPv6 prefix used by the
+   DMR in the MAP-T domain.  Such IPv6 servers (e.g., an HTTP server or
+   a web content cache device) are thus able to serve IPv6 users and
+
+
+
+Li, et al.                   Standards Track                   [Page 15]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   IPv4-only users alike, utilizing IPv6.  Any such IPv6-only servers
+   SHOULD have both A and AAAA records in DNS.  DNS64 [RFC6147] will be
+   required only when IPv6 servers in the MAP-T domain are themselves
+   expected to initiate communication to external IPv4-only hosts.
+
+12.4.  Compatibility with Other NAT64 Solutions
+
+   The MAP-T CE's NAT64 function is by default compatible for use with
+   [RFC6146] stateful NAT64 devices that are placed in the operator's
+   network.  In such a case, the MAP-T CE's DMR prefix is configured to
+   correspond to the NAT64 device prefix.  This in effect allows the use
+   of MAP-T CEs in environments that need to perform statistical
+   multiplexing of IPv4 addresses, while utilizing stateful NAT64
+   devices, and can take the role of a customer-side translator (CLAT)
+   as defined in [RFC6877].
+
+13.  Security Considerations
+
+   Spoofing attacks:  With consistency checks between IPv4 and IPv6
+      sources that are performed on IPv4/IPv6 packets received by MAP
+      nodes, MAP does not introduce any new opportunity for spoofing
+      attacks that would not already exist in IPv6.
+
+   Denial-of-service attacks:  In MAP domains where IPv4 addresses are
+      shared, the fact that IPv4 datagram reassembly may be necessary
+      introduces an opportunity for DoS attacks.  This is inherent in
+      address sharing and is common with other address-sharing
+      approaches such as Dual-Stack Lite (DS-Lite) and NAT64/DNS64.  The
+      best protection against such attacks is to accelerate IPv6 support
+      in both clients and servers.
+
+   Routing loop attacks:  Routing loop attacks may exist in some
+      "automatic tunneling" scenarios and are documented in [RFC6324].
+      They cannot exist with MAP because each BR checks that the IPv6
+      source address of a received IPv6 packet is a CE address based on
+      the Forwarding Mapping Rule.
+
+   Attacks facilitated by restricted port set:  From hosts that are not
+      subject to ingress filtering [RFC2827], an attacker can inject
+      spoofed packets during ongoing transport connections [RFC4953]
+      [RFC5961] [RFC6056].  The attacks depend on guessing which ports
+      are currently used by target hosts.  Using an unrestricted port
+      set is preferable, i.e., using native IPv6 connections that are
+      not subject to MAP port-range restrictions.  To minimize these
+      types of attacks when using a restricted port set, the MAP CE's
+      NAT44 filtering behavior SHOULD be "Address-Dependent Filtering"
+      as described in Section 5 of [RFC4787].  Furthermore, the MAP CEs
+      SHOULD use a DNS transport proxy function to handle DNS traffic
+
+
+
+Li, et al.                   Standards Track                   [Page 16]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+      and source such traffic from IPv6 interfaces not assigned to
+      MAP-T.  Practicalities of these methods are discussed in
+      Section 5.9 of [Stateless-4Via6].
+
+   ICMP Flooding:  Given the necessity to process and translate ICMP and
+      ICMPv6 messages by the BR and CE nodes, a foreseeable attack
+      vector is that of a flood of such messages leading to a saturation
+      of the node's ICMP computing resources.  This attack vector is not
+      specific to MAP, and its mitigation lies in a combination of
+      policing the rate of ICMP messages, policing the rate at which
+      such messages can get processed by the MAP nodes, and of course
+      identifying and blocking off the source(s) of such traffic.
+
+   [RFC6269] outlines general issues with IPv4 address sharing.
+
+14.  References
+
+14.1.  Normative References
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119,
+              DOI 10.17487/RFC2119, March 1997,
+              <http://www.rfc-editor.org/info/rfc2119>.
+
+   [RFC6052]  Bao, C., Huitema, C., Bagnulo, M., Boucadair, M., and X.
+              Li, "IPv6 Addressing of IPv4/IPv6 Translators", RFC 6052,
+              DOI 10.17487/RFC6052, October 2010,
+              <http://www.rfc-editor.org/info/rfc6052>.
+
+   [RFC6145]  Li, X., Bao, C., and F. Baker, "IP/ICMP Translation
+              Algorithm", RFC 6145, DOI 10.17487/RFC6145, April 2011,
+              <http://www.rfc-editor.org/info/rfc6145>.
+
+   [RFC6346]  Bush, R., Ed., "The Address plus Port (A+P) Approach to
+              the IPv4 Address Shortage", RFC 6346,
+              DOI 10.17487/RFC6346, August 2011,
+              <http://www.rfc-editor.org/info/rfc6346>.
+
+   [RFC7597]  Troan, O., Ed., Dec, W., Li, X., Bao, C., Matsushima, S.,
+              Murakami, T., and T. Taylor, Ed., "Mapping of Address and
+              Port with Encapsulation (MAP-E)", RFC 7597,
+              DOI 10.17487/RFC7597, July 2015,
+              <http://www.rfc-editor.org/info/rfc7597>.
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 17]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+14.2.  Informative References
+
+   [MAP-T-Use-Cases]
+              Maglione, R., Ed., Dec, W., Leung, I., and E. Mallette,
+              "Use cases for MAP-T", Work in Progress,
+              draft-maglione-softwire-map-t-scenarios-05, October 2014.
+
+   [RFC792]   Postel, J., "Internet Control Message Protocol", STD 5,
+              RFC 792, DOI 10.17487/RFC0792, September 1981,
+              <http://www.rfc-editor.org/info/rfc792>.
+
+   [RFC879]   Postel, J., "The TCP Maximum Segment Size and Related
+              Topics", RFC 879, DOI 10.17487/RFC0879, November 1983,
+              <http://www.rfc-editor.org/info/rfc879>.
+
+   [RFC2663]  Srisuresh, P. and M. Holdrege, "IP Network Address
+              Translator (NAT) Terminology and Considerations",
+              RFC 2663, DOI 10.17487/RFC2663, August 1999,
+              <http://www.rfc-editor.org/info/rfc2663>.
+
+   [RFC2827]  Ferguson, P. and D. Senie, "Network Ingress Filtering:
+              Defeating Denial of Service Attacks which employ IP Source
+              Address Spoofing", BCP 38, RFC 2827, DOI 10.17487/RFC2827,
+              May 2000, <http://www.rfc-editor.org/info/rfc2827>.
+
+   [RFC3633]  Troan, O. and R. Droms, "IPv6 Prefix Options for Dynamic
+              Host Configuration Protocol (DHCP) version 6", RFC 3633,
+              DOI 10.17487/RFC3633, December 2003,
+              <http://www.rfc-editor.org/info/rfc3633>.
+
+   [RFC4443]  Conta, A., Deering, S., and M. Gupta, Ed., "Internet
+              Control Message Protocol (ICMPv6) for the Internet
+              Protocol Version 6 (IPv6) Specification", RFC 4443,
+              DOI 10.17487/RFC4443, March 2006,
+              <http://www.rfc-editor.org/info/rfc4443>.
+
+   [RFC4787]  Audet, F., Ed., and C. Jennings, "Network Address
+              Translation (NAT) Behavioral Requirements for Unicast
+              UDP", BCP 127, RFC 4787, DOI 10.17487/RFC4787,
+              January 2007, <http://www.rfc-editor.org/info/rfc4787>.
+
+   [RFC4821]  Mathis, M. and J. Heffner, "Packetization Layer Path MTU
+              Discovery", RFC 4821, DOI 10.17487/RFC4821, March 2007,
+              <http://www.rfc-editor.org/info/rfc4821>.
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 18]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   [RFC4862]  Thomson, S., Narten, T., and T. Jinmei, "IPv6 Stateless
+              Address Autoconfiguration", RFC 4862,
+              DOI 10.17487/RFC4862, September 2007,
+              <http://www.rfc-editor.org/info/rfc4862>.
+
+   [RFC4953]  Touch, J., "Defending TCP Against Spoofing Attacks",
+              RFC 4953, DOI 10.17487/RFC4953, July 2007,
+              <http://www.rfc-editor.org/info/rfc4953>.
+
+   [RFC5382]  Guha, S., Ed., Biswas, K., Ford, B., Sivakumar, S., and P.
+              Srisuresh, "NAT Behavioral Requirements for TCP", BCP 142,
+              RFC 5382, DOI 10.17487/RFC5382, October 2008,
+              <http://www.rfc-editor.org/info/rfc5382>.
+
+   [RFC5508]  Srisuresh, P., Ford, B., Sivakumar, S., and S. Guha, "NAT
+              Behavioral Requirements for ICMP", BCP 148, RFC 5508,
+              DOI 10.17487/RFC5508, April 2009,
+              <http://www.rfc-editor.org/info/rfc5508>.
+
+   [RFC5961]  Ramaiah, A., Stewart, R., and M. Dalal, "Improving TCP's
+              Robustness to Blind In-Window Attacks", RFC 5961,
+              DOI 10.17487/RFC5961, August 2010,
+              <http://www.rfc-editor.org/info/rfc5961>.
+
+   [RFC6056]  Larsen, M. and F. Gont, "Recommendations for Transport-
+              Protocol Port Randomization", BCP 156, RFC 6056,
+              DOI 10.17487/RFC6056, January 2011,
+              <http://www.rfc-editor.org/info/rfc6056>.
+
+   [RFC6146]  Bagnulo, M., Matthews, P., and I. van Beijnum, "Stateful
+              NAT64: Network Address and Protocol Translation from IPv6
+              Clients to IPv4 Servers", RFC 6146, DOI 10.17487/RFC6146,
+              April 2011, <http://www.rfc-editor.org/info/rfc6146>.
+
+   [RFC6147]  Bagnulo, M., Sullivan, A., Matthews, P., and I. van
+              Beijnum, "DNS64: DNS Extensions for Network Address
+              Translation from IPv6 Clients to IPv4 Servers", RFC 6147,
+              DOI 10.17487/RFC6147, April 2011,
+              <http://www.rfc-editor.org/info/rfc6147>.
+
+   [RFC6219]  Li, X., Bao, C., Chen, M., Zhang, H., and J. Wu, "The
+              China Education and Research Network (CERNET) IVI
+              Translation Design and Deployment for the IPv4/IPv6
+              Coexistence and Transition", RFC 6219,
+              DOI 10.17487/RFC6219, May 2011,
+              <http://www.rfc-editor.org/info/rfc6219>.
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 19]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   [RFC6269]  Ford, M., Ed., Boucadair, M., Durand, A., Levis, P., and
+              P. Roberts, "Issues with IP Address Sharing", RFC 6269,
+              DOI 10.17487/RFC6269, June 2011,
+              <http://www.rfc-editor.org/info/rfc6269>.
+
+   [RFC6324]  Nakibly, G. and F. Templin, "Routing Loop Attack Using
+              IPv6 Automatic Tunnels: Problem Statement and Proposed
+              Mitigations", RFC 6324, DOI 10.17487/RFC6324, August 2011,
+              <http://www.rfc-editor.org/info/rfc6324>.
+
+   [RFC6877]  Mawatari, M., Kawashima, M., and C. Byrne, "464XLAT:
+              Combination of Stateful and Stateless Translation",
+              RFC 6877, DOI 10.17487/RFC6877, April 2013,
+              <http://www.rfc-editor.org/info/rfc6877>.
+
+   [RFC7598]  Mrugalski, T., Troan, O., Farrer, I., Perreault, S., Dec,
+              W., Bao, C., Yeh, L., and X. Deng, "DHCPv6 Options for
+              Configuration of Softwire Address and Port-Mapped
+              Clients", RFC 7598, DOI 10.17487/RFC7598, July 2015,
+              <http://www.rfc-editor.org/info/rfc7598>.
+
+   [Solutions-4v6]
+              Boucadair, M., Ed., Matsushima, S., Lee, Y., Bonness, O.,
+              Borges, I., and G. Chen, "Motivations for Carrier-side
+              Stateless IPv4 over IPv6 Migration Solutions", Work in
+              Progress, draft-ietf-softwire-stateless-4v6-motivation-05,
+              November 2012.
+
+   [Stateless-4Via6]
+              Dec, W., Asati, R., Bao, C., Deng, H., and M. Boucadair,
+              "Stateless 4Via6 Address Sharing", Work in Progress,
+              draft-dec-stateless-4v6-04, October 2011.
+
+   [TR069]    Broadband Forum TR-069, "CPE WAN Management Protocol",
+              Amendment 5, CWMP Version: 1.4, November 2013,
+              <https://www.broadband-forum.org>.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 20]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+Appendix A.  Examples of MAP-T Translation
+
+   Example 1 - Basic Mapping Rule:
+
+   Given the following MAP domain information and IPv6 end-user prefix
+   assigned to a MAP CE:
+
+   End-user IPv6 prefix:  2001:db8:0012:3400::/56
+   Basic Mapping Rule:    {2001:db8:0000::/40 (Rule IPv6 prefix),
+                           192.0.2.0/24 (Rule IPv4 prefix),
+                           16 (Rule EA-bit length)}
+   PSID length:           (16 - (32 - 24) = 8 (sharing ratio of 256)
+   PSID offset:           6 (default)
+
+   A MAP node (CE or BR) can, via the BMR or equivalent FMR, determine
+   the IPv4 address and port set as shown below:
+
+   EA bits offset:        40
+   IPv4 suffix bits (p):  Length of IPv4 address (32) -
+                          IPv4 prefix length (24) = 8
+   IPv4 address:          192.0.2.18 (0xc0000212)
+   PSID start:            40 + p = 40 + 8 = 48
+   PSID length (q):       o - p = (End-user prefix len -
+                          Rule IPv6 prefix len) - p
+                          = (56 - 40) - 8 = 8
+   PSID:                  0x34
+
+   Available ports (63 ranges): 1232-1235, 2256-2259, ...... ,
+                                63696-63699, 64720-64723
+
+   The BMR information allows a MAP CE to determine (complete) its
+   IPv6 address within the indicated End-user IPv6 prefix.
+
+   IPv6 address of MAP CE:  2001:db8:0012:3400:0000:c000:0212:0034
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 21]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   Example 2 - BR:
+
+   Another example is a MAP-T BR configured with the following FMR
+   when receiving a packet with the following characteristics:
+
+   IPv4 source address:       10.2.3.4 (0x0a020304)
+   TCP source port:           80
+   IPv4 destination address:  192.0.2.18 (0xc0000212)
+   TCP destination port:      1232
+
+   Forwarding Mapping Rule:   {2001:db8::/40 (Rule IPv6 prefix),
+                               192.0.2.0/24 (Rule IPv4 prefix),
+                               16 (Rule EA-bit length)}
+
+   MAP-T BR Prefix (DMR):     2001:db8:ffff::/64
+
+   The above information allows the BR to derive the mapped destination
+   IPv6 address for the corresponding MAP-T CE, and also the source
+   IPv6 address for the mapped IPv4 source address, as follows:
+
+   IPv4 suffix bits (p):     32 - 24 = 8 (18 (0x12))
+   PSID length:              8
+   PSID:  0                  x34 (1232)
+
+   The resulting IPv6 packet will have the following header fields:
+
+   IPv6 source address:      2001:db8:ffff:0:000a:0203:0400::
+   IPv6 destination address: 2001:db8:0012:3400:0000:c000:0212:0034
+   TCP source port:          80
+   TCP destination port:     1232
+
+
+   Example 3 - FMR:
+
+   An IPv4 host behind a MAP-T CE (configured as per the previous
+   examples) corresponding with IPv4 host 10.2.3.4 will have its
+   packets converted into IPv6 using the DMR configured on the MAP-T
+   CE as follows:
+
+   Default Mapping Rule:         {2001:db8:ffff::/64 (Rule IPv6 prefix),
+                                  0.0.0.0/0 (Rule IPv4 prefix)}
+
+   IPv4 source address:          192.0.2.18
+   IPv4 destination address:     10.2.3.4
+   IPv4 source port:             1232
+   IPv4 destination port:        80
+   MAP-T CE IPv6 source address: 2001:db8:0012:3400:0000:c000:0212:0034
+   IPv6 destination address:     2001:db8:ffff:0:000a:0203:0400::
+
+
+
+Li, et al.                   Standards Track                   [Page 22]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   Example 4 - Rule with no embedded address bits and no address
+   sharing:
+
+   End-user IPv6 prefix:    2001:db8:0012:3400::/56
+   Basic Mapping Rule:      {2001:db8:0012:3400::/56 (Rule IPv6 prefix),
+                             192.0.2.1/32 (Rule IPv4 prefix),
+                             0 (Rule EA-bit length)}
+   PSID length:             0 (sharing ratio is 1)
+   PSID offset:             n/a
+
+   A MAP node can, via the BMR or equivalent FMR, determine the
+   IPv4 address and port set as shown below:
+
+   EA bits offset:          0
+   IPv4 suffix bits (p):    Length of IPv4 address -
+                            IPv4 prefix length = 32 - 32 = 0
+   IPv4 address:            192.0.2.18 (0xc0000212)
+   PSID start:              0
+   PSID length:             0
+   PSID:                    null
+
+   The BMR information allows a MAP CE to also determine (complete) its
+   full IPv6 address by combining the IPv6 prefix with the MAP interface
+   identifier (that embeds the IPv4 address).
+
+   IPv6 address of MAP CE:  2001:db8:0012:3400:0000:c000:0201:0000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 23]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   Example 5 - Rule with no embedded address bits and address sharing
+   (sharing ratio of 256):
+
+   End-user IPv6 prefix:    2001:db8:0012:3400::/56
+   Basic Mapping Rule:      {2001:db8:0012:3400::/56 (Rule IPv6 prefix),
+                             192.0.2.18/32 (Rule IPv4 prefix),
+                             0 (Rule EA-bit length)}
+   PSID length:             (16 - (32 - 24)) = 8 (sharing ratio of 256;
+                            provisioned with DHCPv6)
+   PSID offset:             6 (default)
+   PSID:                    0x20 (provisioned with DHCPv6)
+
+   A MAP node can, via the BMR, determine the IPv4 address and port set
+   as shown below:
+
+   EA bits offset:          0
+   IPv4 suffix bits (p):    Length of IPv4 address -
+                            IPv4 prefix length = 32 - 32 = 0
+   IPv4 address             192.0.2.18 (0xc0000212)
+   PSID start:              0
+   PSID length:             8
+   PSID:                    0x34
+
+   Available ports (63 ranges): 1232-1235, 2256-2259, ...... ,
+                                63696-63699, 64720-64723
+
+   The BMR information allows a MAP CE to also determine (complete) its
+   full IPv6 address by combining the IPv6 prefix with the MAP interface
+   identifier (that embeds the IPv4 address and PSID).
+
+   IPv6 address of MAP CE:  2001:db8:0012:3400:0000:c000:0212:0034
+
+   Note that the IPv4 address and PSID are not derived from the IPv6
+   prefix assigned to the CE but are provisioned separately, using, for
+   example, MAP options in DHCPv6.
+
+Appendix B.  Port-Mapping Algorithm
+
+   The driving principles and the mathematical expression of the mapping
+   algorithm used by MAP can be found in Appendix B of [RFC7597].
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 24]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+Acknowledgements
+
+   This document is based on the ideas of many, particularly Remi
+   Despres, who has tirelessly worked on generalized mechanisms for
+   stateless address mapping.
+
+   The authors would also like to thank Mohamed Boucadair, Guillaume
+   Gottard, Dan Wing, Jan Zorz, Nejc Skoberne, Tina Tsou, Gang Chen,
+   Maoke Chen, Xiaohong Deng, Jouni Korhonen, Tomek Mrugalski, Jacni
+   Qin, Chunfa Sun, Qiong Sun, Leaf Yeh, Andrew Yourtchenko, Roberta
+   Maglione, and Hongyu Chen for their review and comments.
+
+Contributors
+
+   The following individuals authored major contributions to this
+   document and made the document possible:
+
+   Chongfeng Xie
+   China Telecom
+   Room 708, No. 118, Xizhimennei Street
+   Beijing  100035
+   China
+   Phone: +86-10-58552116
+   Email: xiechf@ctbri.com.cn
+
+   Qiong Sun
+   China Telecom
+   Room 708, No. 118, Xizhimennei Street
+   Beijing  100035
+   China
+   Phone: +86-10-58552936
+   Email: sunqiong@ctbri.com.cn
+
+   Rajiv Asati
+   Cisco Systems
+   7025-6 Kit Creek Road
+   Research Triangle Park, NC  27709
+   United States
+   Email: rajiva@cisco.com
+
+   Gang Chen
+   China Mobile
+   29, Jinrong Avenue
+   Xicheng District, Beijing  100033
+   China
+   Email: phdgang@gmail.com, chengang@chinamobile.com
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 25]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   Wentao Shang
+   CERNET Center/Tsinghua University
+   Room 225, Main Building, Tsinghua University
+   Beijing  100084
+   China
+   Email: wentaoshang@gmail.com
+
+   Guoliang Han
+   CERNET Center/Tsinghua University
+   Room 225, Main Building, Tsinghua University
+   Beijing  100084
+   China
+   Email: bupthgl@gmail.com
+
+   Yu Zhai
+   CERNET Center/Tsinghua University
+   Room 225, Main Building, Tsinghua University
+   Beijing  100084
+   China
+   Email: jacky.zhai@gmail.com
+
+Authors' Addresses
+
+   Xing Li
+   CERNET Center/Tsinghua University
+   Room 225, Main Building, Tsinghua University
+   Beijing  100084
+   China
+
+   Email: xing@cernet.edu.cn
+
+
+   Congxiao Bao
+   CERNET Center/Tsinghua University
+   Room 225, Main Building, Tsinghua University
+   Beijing  100084
+   China
+
+   Email: congxiao@cernet.edu.cn
+
+
+   Wojciech Dec (editor)
+   Cisco Systems
+   Haarlerbergpark Haarlerbergweg 13-19
+   Amsterdam, NOORD-HOLLAND  1101 CH
+   The Netherlands
+
+   Email: wdec@cisco.com
+
+
+
+Li, et al.                   Standards Track                   [Page 26]
+\f
+RFC 7599                          MAP-T                        July 2015
+
+
+   Ole Troan
+   Cisco Systems
+   Philip Pedersens vei 1
+   Lysaker  1366
+   Norway
+
+   Email: ot@cisco.com
+
+
+   Satoru Matsushima
+   SoftBank Telecom
+   1-9-1 Higashi-Shinbashi, Munato-ku
+   Tokyo
+   Japan
+
+   Email: satoru.matsushima@g.softbank.co.jp
+
+
+   Tetsuya Murakami
+   IP Infusion
+   1188 East Arques Avenue
+   Sunnyvale, CA  94085
+   United States
+
+   Email: tetsuya@ipinfusion.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Li, et al.                   Standards Track                   [Page 27]
+\f
index 4b32e6a..3834cb3 100644 (file)
@@ -67,4 +67,7 @@ attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' DESC ''
 attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-objectClasses: ( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SUP top AUXILIARY MUST ( cn ) MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDn $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess ) )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.60 NAME 'radiusControlAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusReplyAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusRequestAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+objectClasses: ( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SUP top AUXILIARY MUST ( cn ) MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDn $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute ) )
index f599c55..6392b02 100644 (file)
@@ -13,7 +13,7 @@ cn: schema
 #
 aci: (target="ldap:///cn=schema")(targetattr !="aci")(version 3.0;acl "anonymous, no acis"; allow (read, search, compare) userdn = "ldap:///anyone";
 #######################
-objectClasses: ( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' SUP top AUXILIARY DESC 'Free Radius schema for Directory Server 5.2' MUST (cn) MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess) X-ORIGIN 'user defined')
+objectClasses: ( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' SUP top AUXILIARY DESC 'Free Radius schema for Directory Server 5.2' MUST (cn) MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess $ radiusControlAttribute $ radiusReplyAttribute $radiusRequestAttribute ) X-ORIGIN 'user defined')
 attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.1 NAME 'radiusArapFeatures' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined')
 attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.2 NAME 'radiusArapSecurity' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined')
 attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.3 NAME 'radiusArapZoneAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined')
@@ -73,3 +73,6 @@ attributeTypes:  ( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' DESC ''
 attributeTypes:  ( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
 attributeTypes:  ( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
 attributeTypes:  ( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributetypes:  ( 1.3.6.1.4.1.11344.4.3.1.60 NAME 'radiusControlAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributetypes:  ( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusReplyAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributetypes:  ( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusRequestAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
index b4059f0..44d2cb9 100644 (file)
@@ -60,8 +60,11 @@ olcAttributeTypes: {55}( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' D
 olcAttributeTypes: {56}( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 olcAttributeTypes: {57}( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC 'controlItem: Expiration' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 olcAttributeTypes: {58}( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
-olcAttributeTypes: {60}( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusNASIpAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
-olcAttributeTypes: {61}( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusReplyMessage' DESC 'replyItem: Reply-Message' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {59}( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusNASIpAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {60}( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusReplyMessage' DESC 'replyItem: Reply-Message' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {61}( 1.3.6.1.4.1.11344.4.3.1.63 NAME 'radiusControlAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {62}( 1.3.6.1.4.1.11344.4.3.1.64 NAME 'radiusReplyAttribute' DESC 'replyItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {63}( 1.3.6.1.4.1.11344.4.3.1.65 NAME 'radiusRequestAttribute' DESC 'requestItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
 olcObjectClasses: {0}( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SUP top AUXILIARY MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $
  radiusCallbackId $ radiusCallbackNumber $radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $
  radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $radiusFramedProtocol $ radiusAttribute $
@@ -69,5 +72,5 @@ olcObjectClasses: {0}( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SU
  radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $
  radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $
  radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $
- dialupAccess $ radiusNASIpAddress $ radiusReplyMessage ) )
+ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute ) )
 olcObjectClasses: {1}( 1.3.6.1.4.1.11344.4.3.2.2 NAME 'radiusObjectProfile' DESC 'A Container Objectclass to be used for creating radius profile object' SUP top STRUCTURAL MUST cn MAY ( uid $ userPassword $ description ) )
index 7bd2d98..921de75 100644 (file)
@@ -439,11 +439,32 @@ attributetype ( 1.3.6.1.4.1.11344.4.3.1.62
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
  )
 
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.63
+       NAME 'radiusControlAttribute'
+       DESC 'controlItem: $GENERIC$'
+       EQUALITY caseIgnoreIA5Match
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.64
+       NAME 'radiusReplyAttribute'
+       DESC 'replyItem: $GENERIC$'
+       EQUALITY caseIgnoreIA5Match
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.65
+       NAME 'radiusRequestAttribute'
+       DESC 'requestItem: $GENERIC$'
+       EQUALITY caseIgnoreIA5Match
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
 objectclass ( 1.3.6.1.4.1.11344.4.3.2.1
        NAME 'radiusprofile'
        SUP top
        AUXILIARY
-       MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage )
+       MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute )
  )
 
 objectclass ( 1.3.6.1.4.1.11344.4.3.2.2
diff --git a/doc/schemas/logstash/README b/doc/schemas/logstash/README
new file mode 100644 (file)
index 0000000..93b937c
--- /dev/null
@@ -0,0 +1,70 @@
+Example configuration for logstash/elasticsearch
+================================================
+
+So you've got all these RADIUS logs, but how do you analyse them? What is the
+easiest way to query the logs, find out when a client connected or
+disconnected, or view the top ten clients logging into the system over the last
+six hours?
+
+The logstash/elasticsearch/kibana stack is designed and built to do just that.
+elasticsearch is a search engine; logstash is commonly used to feed data in,
+and kibana the web interface to query the logs in near real time.
+
+Installing the ELK stack is beyond the scope of this document, but can be done
+in a short amount of time by any competent sysadmin. Then comes getting the
+logs in.
+
+This directory contains the following files as a starting point for feeding
+RADIUS logs into elasticsearch via logstash.
+
+Files
+-----
+
+Please note that all files should be reviewed before use to determine if they
+are suitable for your configuration/system.
+
+radius-mapping.sh
+
+  Each elasticsearch index needs a mapping to describe how fields are stored.
+  If one is not provided then all is not lost as elasticsearch will build one
+  on the fly. However, this may not be optimal, especially for RADIUS data, as
+  all fields will be analyzed making some visualisations hard or impossible
+  (such as showing top N clients).
+
+  This shell script (which just runs curl) pushes a template mapping into the
+  elasticsearch cluster.
+
+
+radius.conf
+
+  A sample configuration file for logstash that parses RADIUS 'detail' files.
+  It processes these by joining each record onto one line, then splitting the
+  tab-delimited key-value pairs out.
+
+  The file will need to be edited at least to set the input method: for
+  experimentation the given input (stdin) may be used. If logstash is running on
+  the RADIUS server then 'file' input may be appropriate, otherwise a different
+  input such as log-courier or logstash-forwarder may be better to get the data
+  over the network to the logstash server.
+
+
+Example usage
+-------------
+
+Install mapping (only needs to be done once):
+$ ./radius-mapping.sh
+
+Feed a detail file in:
+$ /path/to/logstash -f radius.conf < acct-detail
+
+
+See also
+--------
+
+elasticsearch web site: http://www.elastic.co/
+
+
+
+Matthew Newton
+April 2015
+
diff --git a/doc/schemas/logstash/radius-mapping.sh b/doc/schemas/logstash/radius-mapping.sh
new file mode 100644 (file)
index 0000000..25f7071
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/sh
+
+# Create a template mapping for RADIUS data
+# Matthew Newton
+# April 2015
+
+# This should be run on an elasticsearch node. Alternatively, adjust
+# the curl URI below.
+
+curl -XPUT '127.0.0.1:9200/_template/radius' -d '
+{
+  "template":"radius-*",
+  "order":0,
+  "mappings":{
+    "detail":{
+      "dynamic_templates":[
+        { "keep_message":{
+            "match":"message",
+            "mapping":{
+              "type":"string",
+              "index":"analyzed"
+            }
+          }
+        },
+        { "no_analyze_strings":{
+            "match":"*",
+            "match_mapping_type":"string",
+            "mapping":{
+              "type":"string",
+              "index":"not_analyzed"
+            }
+          }
+        }
+      ]
+    }
+  }
+}'
+
diff --git a/doc/schemas/logstash/radius.conf b/doc/schemas/logstash/radius.conf
new file mode 100644 (file)
index 0000000..a837fa3
--- /dev/null
@@ -0,0 +1,77 @@
+# logstash configuration to process RADIUS detail files
+#
+# Matthew Newton
+# February 2014
+# 
+# RADIUS "detail" files are textual representations of the RADIUS
+# packets, and are written to disk by e.g. FreeRADIUS. They look
+# something like the following, with the timestamp on the first
+# line then all attributes/values tab-indented.
+#
+#      Tue Mar 10 15:32:24 2015
+#              Packet-Type = Access-Request
+#              User-Name = "test@example.com"
+#              Calling-Station-Id = "01-02-03-04-05-06"
+#              Called-Station-Id = "aa-bb-cc-dd-ee-ff:myssid"
+#              NAS-Port = 10
+#              NAS-IP-Address = 10.9.0.4
+#              NAS-Identifier = "Wireless-Controller-1"
+#              Service-Type = Framed-User
+#              NAS-Port-Type = Wireless-802.11
+#
+# This filter processes the detail file such that each attribute
+# is stored as a separate field in the output document.
+
+
+input {
+  stdin {
+    type => radiusdetail
+  }
+}
+
+
+filter {
+
+    if [type] == "radiusdetail" {
+
+       # join all lines of a record together
+       multiline {
+               pattern => "^[^\t]"
+               negate => true
+               what => "previous"
+       }
+
+       # pull off the timestamp
+       grok {
+               match => [ "message", "^(?<timestamp>[^\n\t]+)[\n\t]" ]
+       }
+
+       # create the timestamp field
+       date {
+               match => [ "timestamp", "EEE MMM dd HH:mm:ss yyyy",
+                                       "EEE MMM  d HH:mm:ss yyyy" ]
+       }
+
+       # split the attributes and values into fields
+       kv {
+               field_split => "\n"
+               source => "message"
+               trim => "\" "
+               trimkey => "\t "
+       }
+    }
+}
+
+output {
+  if [type] == "radiusdetail" {
+    elasticsearch {
+      host => localhost
+      protocol => http
+      cluster => elasticsearch
+      index_type => "detail"
+      index => "radius-%{+YYYY.MM.dd}"
+      flush_size => 1000
+    }
+  }
+}
+
index 76e6b57..010a21f 100644 (file)
-# Doxyfile 1.7.5.1
+# Doxyfile 1.8.9
 
 # This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
 #
-# All text after a hash (#) is considered a comment and will be ignored
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
 # The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
 
 #---------------------------------------------------------------------------
 # Project related configuration options
 #---------------------------------------------------------------------------
 
 # This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
-# to put quotes around the project name if it contains spaces.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
 
 PROJECT_NAME           = FreeRADIUS
 
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
 
 PROJECT_NUMBER         = $Id$
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# quick idea about the purpose of the project. Keep the description short.
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
 
 PROJECT_BRIEF          =
 
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
 
 PROJECT_LOGO           = ./extra/freeradius.png
 
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
 
 OUTPUT_DIRECTORY       =
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
 
 CREATE_SUBDIRS         = NO
 
 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
 # documentation generated by doxygen is written. Doxygen will use this
 # information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
 
 BRIEF_MEMBER_DESC      = YES
 
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
 # brief descriptions will be completely suppressed.
+# The default value is: YES.
 
 REPEAT_BRIEF           = YES
 
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
 
 ABBREVIATE_BRIEF       =
 
 # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# doxygen will generate a detailed section even if there is only a brief
 # description.
+# The default value is: NO.
 
 ALWAYS_DETAILED_SEC    = NO
 
@@ -112,545 +123,678 @@ ALWAYS_DETAILED_SEC    = NO
 # inherited members of a class in the documentation of that class as if those
 # members were ordinary class members. Constructors, destructors and assignment
 # operators of the base classes will not be shown.
+# The default value is: NO.
 
 INLINE_INHERITED_MEMB  = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
 
 FULL_PATH_NAMES        = YES
 
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
 
 STRIP_FROM_PATH        =
 
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
 
 STRIP_FROM_INC_PATH    =
 
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
 
 SHORT_NAMES            = NO
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
 
 JAVADOC_AUTOBRIEF      = YES
 
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
 
 QT_AUTOBRIEF           = NO
 
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
 
 MULTILINE_CPP_IS_BRIEF = NO
 
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
 
 INHERIT_DOCS           = YES
 
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
 
 SEPARATE_MEMBER_PAGES  = NO
 
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
 
 TAB_SIZE               = 8
 
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
 
 ALIASES                =
 
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_FOR_C  = YES
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
 # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
 
 OPTIMIZE_FOR_FORTRAN   = NO
 
 # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
 # Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
 
 EXTENSION_MAPPING      =
 
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
 
 BUILTIN_STL_SUPPORT    = NO
 
 # If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
+# The default value is: NO.
 
 CPP_CLI_SUPPORT        = NO
 
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
 
 IDL_PROPERTY_SUPPORT   = YES
 
 # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
+# tag is set to YES then doxygen will reuse the documentation of the first
 # member in the group (if any) for the other members of the group. By default
 # all members of a group must be documented explicitly.
+# The default value is: NO.
 
 DISTRIBUTE_GROUP_DOC   = NO
 
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
 
 SUBGROUPING            = YES
 
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
-# section (for LaTeX and RTF).
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
 
 INLINE_GROUPED_CLASSES = NO
 
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields will be shown inline in the documentation
-# of the scope in which they are defined (i.e. file, namespace, or group
-# documentation), provided this scope is documented. If set to NO (the default),
-# structs, classes, and unions are shown on a separate page (for HTML and Man
-# pages) or section (for LaTeX and RTF).
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
 
 INLINE_SIMPLE_STRUCTS  = YES
 
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
 # typedef struct TypeS {} TypeT, will appear in the documentation as a struct
 # with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
 # types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
 
-TYPEDEF_HIDES_STRUCT   = YES
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 2
 
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
 
 EXTRACT_ALL            = YES
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
 
 EXTRACT_PRIVATE        = NO
 
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
 
 EXTRACT_STATIC         = YES
 
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
 
 EXTRACT_LOCAL_CLASSES  = YES
 
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
 
 EXTRACT_LOCAL_METHODS  = NO
 
 # If this flag is set to YES, the members of anonymous namespaces will be
 # extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
 
 EXTRACT_ANON_NSPACES   = YES
 
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_MEMBERS     = NO
 
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
 
 HIDE_UNDOC_CLASSES     = NO
 
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
 
 HIDE_IN_BODY_DOCS      = NO
 
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
 
 INTERNAL_DOCS          = NO
 
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
 # allowed. This is useful if you have classes or files whose names only differ
 # in case and if your file system supports case sensitive file names. Windows
 # and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
 
 CASE_SENSE_NAMES       = YES
 
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
 
 HIDE_SCOPE_NAMES       = NO
 
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
 
 SHOW_INCLUDE_FILES     = YES
 
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
 
 FORCE_LOCAL_INCLUDES   = NO
 
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
 
 INLINE_INFO            = YES
 
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
 
 SORT_MEMBER_DOCS       = YES
 
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
 
-SORT_BRIEF_DOCS        = NO
+SORT_BRIEF_DOCS        = YES
 
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
 
 SORT_MEMBERS_CTORS_1ST = NO
 
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
 
-SORT_GROUP_NAMES       = NO
+SORT_GROUP_NAMES       = YES
 
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
 
 SORT_BY_SCOPE_NAME     = NO
 
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
 
 STRICT_PROTO_MATCHING  = NO
 
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
 
 GENERATE_TODOLIST      = YES
 
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
 
 GENERATE_TESTLIST      = YES
 
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
 
 GENERATE_BUGLIST       = YES
 
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
 
 GENERATE_DEPRECATEDLIST= YES
 
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
 
 ENABLED_SECTIONS       =
 
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
 
 MAX_INITIALIZER_LINES  = 30
 
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
 # list will mention the files that were used to generate the documentation.
+# The default value is: YES.
 
 SHOW_USED_FILES        = 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.
+# 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 value is: YES.
 
 SHOW_FILES             = YES
 
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.  This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
 
 SHOW_NAMESPACES        = YES
 
 # The FILE_VERSION_FILTER tag can be used to specify a program or script that
 # doxygen should invoke to get the current version for each file (typically from
 # the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
 
 FILE_VERSION_FILTER    =
 
 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
 # by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. The create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
 
 LAYOUT_FILE            =
 
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE.
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
 
 CITE_BIB_FILES         =
 
 #---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
 
 QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
 
 WARNINGS               = YES
 
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
 
 WARN_IF_UNDOCUMENTED   = YES
 
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
 
 WARN_IF_DOC_ERROR      = YES
 
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
 
 WARN_NO_PARAMDOC       = NO
 
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
 
 WARN_FORMAT            = "$file:$line: $text"
 
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
 
 WARN_LOGFILE           =
 
 #---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
 
-INPUT                  = ../../src extra
+INPUT                  = ../../src \
+                         extra
 
 # This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
 
 INPUT_ENCODING         = UTF-8
 
 # If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
 
 FILE_PATTERNS          = *.c \
                          *.h
 
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
 
 RECURSIVE              = YES
 
-# The EXCLUDE tag can be used to specify files and/or directories that should
+# The EXCLUDE tag can be used to specify files and/or directories that should be
 # excluded from the INPUT source files. This way you can easily exclude a
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
-# Note that relative paths are relative to directory from which doxygen is run.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
 
 EXCLUDE                =
 
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
+# The default value is: NO.
 
 EXCLUDE_SYMLINKS       = NO
 
 # If the value of the INPUT tag contains directories, you can use the
 # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
 
 EXCLUDE_PATTERNS       =
 
@@ -659,735 +803,1073 @@ EXCLUDE_PATTERNS       =
 # output. The symbol name can be a fully qualified name, a word, or if the
 # wildcard * is used, a substring. Examples: ANamespace, AClass,
 # AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
 
 EXCLUDE_SYMBOLS        =
 
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
 
 EXAMPLE_PATH           =
 
 # If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
 
 EXAMPLE_PATTERNS       =
 
 # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
 
 EXAMPLE_RECURSIVE      = NO
 
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
 
 IMAGE_PATH             =
 
 # The INPUT_FILTER tag can be used to specify a program that doxygen should
 # invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be
-# ignored.
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
 
 INPUT_FILTER           =
 
 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.  Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.  The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
 
 FILTER_PATTERNS        =
 
 # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
 
 FILTER_SOURCE_FILES    = NO
 
 # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
 
 FILTER_SOURCE_PATTERNS =
 
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
 #---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
 
 SOURCE_BROWSER         = YES
 
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
 
 INLINE_SOURCES         = NO
 
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
 
 STRIP_CODE_COMMENTS    = NO
 
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
 
-REFERENCED_BY_RELATION = YES
+REFERENCED_BY_RELATION = NO
 
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
 
 REFERENCES_RELATION    = NO
 
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.  Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
 
 REFERENCES_LINK_SOURCE = YES
 
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
 
 USE_HTAGS              = NO
 
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
 
 VERBATIM_HEADERS       = YES
 
 #---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
 
 ALPHABETICAL_INDEX     = YES
 
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
 COLS_IN_ALPHA_INDEX    = 5
 
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
 IGNORE_PREFIX          =
 
 #---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
 
 GENERATE_HTML          = YES
 
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_OUTPUT            = html
 
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_FILE_EXTENSION    = .html
 
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-# for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is adviced to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
-# changing the value of configuration settings such as GENERATE_TREEVIEW!
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_HEADER            =
 
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_FOOTER            =
 
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# stylesheet in the HTML output directory as well, or it will be erased!
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_STYLESHEET        =
 
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
 # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
 # other source files which should be copied to the HTML output directory. Note
 # that these files will be copied to the base HTML output directory. Use the
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
-# the files will be copied as-is; there are no commands or markers available.
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_EXTRA_FILES       =
 
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the stylesheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_COLORSTYLE_HUE    = 220
 
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_COLORSTYLE_SAT    = 100
 
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_COLORSTYLE_GAMMA  = 80
 
 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_TIMESTAMP         = 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
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 HTML_DYNAMIC_SECTIONS  = YES
 
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
 # for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_DOCSET        = NO
 
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
 # the documentation publisher. This should be a reverse domain-name style
 # string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
 
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
 
 DOCSET_PUBLISHER_NAME  = Publisher
 
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_HTMLHELP      = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
 # written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 CHM_FILE               =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 HHC_LOCATION           =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 GENERATE_CHI           = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 CHM_INDEX_ENCODING     =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 BINARY_TOC             = NO
 
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
 TOC_EXPAND             = NO
 
 # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_QHP           = NO
 
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QCH_FILE               =
 
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_NAMESPACE          = org.doxygen.Project
 
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_VIRTUAL_FOLDER     = doc
 
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_CUST_FILTER_NAME   =
 
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_CUST_FILTER_ATTRS  =
 
 # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHP_SECT_FILTER_ATTRS  =
 
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
 
 QHG_LOCATION           =
 
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_ECLIPSEHELP   = NO
 
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
 
 ECLIPSE_DOC_ID         = org.doxygen.Project
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 DISABLE_INDEX          = NO
 
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
-# values from appearing in the overview section.
-
-ENUM_VALUES_PER_LINE   = 1
-
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 GENERATE_TREEVIEW      = NO
 
-# 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.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 1
+
+# 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.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 TREEVIEW_WIDTH         = 250
 
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 EXT_LINKS_IN_WINDOW    = NO
 
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 FORMULA_FONTSIZE       = 10
 
 # Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 FORMULA_TRANSPARENT    = YES
 
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 USE_MATHJAX            = NO
 
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to the
-# mathjax.org site, so you can quickly see the result without installing
-# MathJax, but it is strongly recommended to install a local copy of MathJax
-# before deployment.
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
 
 MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
 
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
-# names that should be enabled during MathJax rendering.
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
 
 MATHJAX_EXTENSIONS     =
 
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
 
 SEARCHENGINE           = YES
 
 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvantages are that it is more difficult to setup
-# and does not have live searching capabilities.
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
 
 SERVER_BASED_SEARCH    = NO
 
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
 #---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
 
 GENERATE_LATEX         = NO
 
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_OUTPUT           = latex
 
 # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_CMD_NAME         = latex
 
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 COMPACT_LATEX          = NO
 
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4wide will be used.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 PAPER_TYPE             = a4
 
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 EXTRA_PACKAGES         =
 
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HEADER           =
 
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
-# standard footer. Notice: only use this tag if you know what you are doing!
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_FOOTER           =
 
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 PDF_HYPERLINKS         = YES
 
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
 # higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 USE_PDFLATEX           = YES
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_BATCHMODE        = NO
 
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_HIDE_INDICES     = NO
 
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_SOURCE_CODE      = NO
 
 # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
-# http://en.wikipedia.org/wiki/BibTeX for more info.
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
 
 LATEX_BIB_STYLE        = plain
 
 #---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
 
 GENERATE_RTF           = NO
 
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 COMPACT_RTF            = NO
 
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_STYLESHEET_FILE    =
 
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
 
 RTF_EXTENSIONS_FILE    =
 
 #---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
 
 GENERATE_MAN           = NO
 
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_OUTPUT             = man
 
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_EXTENSION          = .3
 
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
 
 MAN_LINKS              = NO
 
 #---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
 
 GENERATE_XML           = NO
 
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
 
-XML_SCHEMA             =
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
 
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
 
-XML_DTD                =
+GENERATE_DOCBOOK       = NO
 
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
 
-XML_PROGRAMLISTING     = YES
+DOCBOOK_OUTPUT         = docbook
 
 #---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# Configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
 
 GENERATE_AUTOGEN_DEF   = NO
 
 #---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
 
 GENERATE_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.  This is useful
-# if you want to understand what is going on.  On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_PRETTY         = YES
 
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
 
 PERLMOD_MAKEVAR_PREFIX =
 
@@ -1395,122 +1877,142 @@ PERLMOD_MAKEVAR_PREFIX =
 # Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
 
 ENABLE_PREPROCESSING   = YES
 
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 MACRO_EXPANSION        = NO
 
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 EXPAND_ONLY_PREDEF     = NO
 
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# pointed to by INCLUDE_PATH will be searched when a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 SEARCH_INCLUDES        = YES
 
 # The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
 
 INCLUDE_PATH           = ./../../src/include
 
 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
 # patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 INCLUDE_FILE_PATTERNS  = *.h
 
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED            += WITH_PROXY
-PREDEFINED            += WITH_UNLANG
-PREDEFINED            += WITH_ACCOUNTING
-PREDEFINED            += WITH_DETAIL
-PREDEFINED            += WITH_SESSION_MGMT
-PREDEFINED            += WITH_DYNAMIC_CLIENTS
-PREDEFINED            += WITH_STATS
-PREDEFINED            += WITH_COMMAND_SOCKET
-PREDEFINED            += WITH_COA
-PREDEFINED            += WITH_TCP
-PREDEFINED            += WITH_DHCP
-PREDEFINED            += WITH_VMPS
-PREDEFINED            += WITH_THREADS
-PREDEFINED            += HAVE_JSON
-
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = WITH_PROXY \
+                         WITH_UNLANG \
+                         WITH_ACCOUNTING \
+                         WITH_DETAIL \
+                         WITH_SESSION_MGMT \
+                         WITH_DYNAMIC_CLIENTS \
+                         WITH_STATS \
+                         WITH_COMMAND_SOCKET \
+                         WITH_COA \
+                         WITH_TCP \
+                         WITH_DHCP \
+                         WITH_VMPS \
+                         WITH_THREADS \
+                         HAVE_JSON
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 EXPAND_AS_DEFINED      =
 
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
 SKIP_FUNCTION_MACROS   = YES
 
 #---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles.
-# Optionally an initial location of the external documentation
-# can be added for each tagfile. The format of a tag file without
-# this location is as follows:
-#   TAGFILES = file1 file2 ...
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
 # Adding location for the tag files is done as follows:
-#   TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths or
-# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen
-# is run, you must also specify the path to the tagfile here.
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
 
 TAGFILES               =
 
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
 
 GENERATE_TAGFILE       =
 
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
 
 ALLEXTERNALS           = NO
 
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
 
 EXTERNAL_GROUPS        = YES
 
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
 # The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
 
 PERL_PATH              = /usr/bin/perl
 
@@ -1518,213 +2020,293 @@ PERL_PATH              = /usr/bin/perl
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
 
 CLASS_DIAGRAMS         = YES
 
 # You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
 # documentation. The MSCGEN_PATH tag allows you to specify the directory where
 # the mscgen tool resides. If left empty the tool is assumed to be found in the
 # default search path.
 
 MSCGEN_PATH            =
 
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
 
 HIDE_UNDOC_RELATIONS   = YES
 
 # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
 
 HAVE_DOT               = YES
 
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_NUM_THREADS        = 0
 
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
-# directory containing the font.
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTNAME           = Helvetica
 
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
-# set the path where dot can find it.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_FONTPATH           =
 
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CLASS_GRAPH            = YES
 
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 COLLABORATION_GRAPH    = YES
 
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GROUP_GRAPHS           = YES
 
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
 # collaboration diagrams in a style similar to the OMG's Unified Modeling
 # Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 UML_LOOK               = NO
 
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 TEMPLATE_RELATIONS     = NO
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDE_GRAPH          = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CALL_GRAPH             = YES
 
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 CALLER_GRAPH           = YES
 
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DIRECTORY_GRAPH        = YES
 
 # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible in IE 9+ (other browsers do not have this requirement).
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_IMAGE_FORMAT       = svg
 
 # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
 # enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible. Older versions of IE do not have SVG support.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 INTERACTIVE_SVG        = YES
 
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_PATH               =
 
 # The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOTFILE_DIRS           =
 
 # The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
 
 MSCFILE_DIRS           =
 
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# 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.
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# 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.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_GRAPH_MAX_NODES    = 150
 
-# 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
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# 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 from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large 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.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 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
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_TRANSPARENT        = YES
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
 # files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_MULTI_TARGETS      = YES
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
 
 DOT_CLEANUP            = YES
index c234345..74071bd 100644 (file)
@@ -12,4 +12,6 @@ FreeRADIUS uses a pluggable module infrastructure to extend the
 basic functionality of the server.
 
 Modules in 3.0 are dynamically linked at runtime using dlopen.
+
+@defgroup module_safe module_safe - Internal API functions that are safe for use by modules
 */
index 69df244..34c4ebe 100644 (file)
@@ -23,25 +23,63 @@ of modules in each stage.
 - @subpage module_doc "2. Server modules"
 - @subpage client_doc "3. Client APIs"
 
-@section main_trees GIT Trees
+@section main_branches GIT Branch
 
-@subsection tree_dev Development Tree
+@subsection branch_master Experimental Branch
 
 @code
-git://git.freeradius.org/freeradius-server.git
+git clone git@github.com:FreeRADIUS/freeradius-server.git
 @endcode
 - Web: http://github.com/FreeRADIUS/freeradius-server/tree/master
 
-@subsection tree_2xx 2.x.x stable tree
+@subsection branch_31x 3.1.x feature branch
+
+@note Submit pull requests for new features or modules against this branch.
+
+@code
+git clone git@github.com:FreeRADIUS/freeradius-server.git
+cd freeradius-server
+git fetch origin v3.1.x:v3.1.x
+git checkout v3.1.x
+@endcode
+- Web: http://github.com/FreeRADIUS/freeradius-server/tree/v3.1.x
+
+@subsection branch_30x 3.0.x stable branch
+
+@code
+git clone git@github.com:FreeRADIUS/freeradius-server.git
+cd freeradius-server
+git fetch origin v3.0.x:v3.0.x
+git checkout v3.0.x
+@endcode
+- Web: http://github.com/FreeRADIUS/freeradius-server/tree/v3.0.x
+
+@subsection branch_2xx 2.x.x EOL branch
+
+@note This branch is now permanently feature frozen. New features or modules
+      should be submitted against the v3.1.x branch.
 
 @code
-git clone git://git.freeradius.org/freeradius-server.git
+git clone git@github.com:FreeRADIUS/freeradius-server.git
 cd freeradius-server
 git fetch origin v2.x.x:v2.x.x
 git checkout v2.x.x
 @endcode
 - Web: http://github.com/FreeRADIUS/freeradius-server/tree/v2.x.x
 
+@subsection branch_1xx 1.1.x EOL branch
+
+@note This branch is now permanently feature frozen. New features or modules
+      should be submitted against the v3.1.x branch.
+
+@code
+git clone git@github.com:FreeRADIUS/freeradius-server.git
+cd freeradius-server
+git fetch origin v2.x.x:v2.x.x
+git checkout v2.x.x
+@endcode
+- Web: http://github.com/FreeRADIUS/freeradius-server/tree/v1.1.x
+
 @section main_website Website
 
 - http://www.freeradius.org
index d57105a..c5e041f 100644 (file)
@@ -69,6 +69,24 @@ AC_DEFUN([AX_CC_WDOCUMENTATION_FLAG],[
   ])
 ])
 
+AC_DEFUN([AX_CC_PTHREAD_FLAG],[
+  AC_CACHE_CHECK([for the compiler flag "-pthread"], [ax_cv_cc_pthread_flag],[
+
+    CFLAGS_SAVED=$CFLAGS
+    CFLAGS="$CFLAGS -Werror -pthread"
+
+    AC_LANG_PUSH(C)
+    AC_TRY_COMPILE(
+      [],
+      [return 0;],
+      [ax_cv_cc_pthread_flag="yes"],
+      [ax_cv_cc_pthread_flag="no"])
+    AC_LANG_POP
+
+    CFLAGS="$CFLAGS_SAVED"
+  ])
+])
+
 dnl #
 dnl # Determine the number of system cores we have
 dnl #
index 5f8e1d1..6305411 100644 (file)
@@ -69,7 +69,7 @@ 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.
+if \-s is passed.
 .IP \-F
 Print the file name, packet number and reply code.
 .IP \-h
@@ -79,7 +79,7 @@ Use \fIid\fP as the RADIUS request Id.
 .IP \-n\ \fInum_requests_per_second\fP
 Try to send \fInum_requests_per_second\fP, evenly spaced.  This option
 allows you to slow down the rate at which radclient sends requests.
-When not using -n, the default is to send packets as quickly as
+When not using \-n, the default is to send packets as quickly as
 possible, with no inter-packet delays.
 
 Due to limitations in radclient, this option does not accurately send
index 0b8d221..c131255 100644 (file)
@@ -35,7 +35,7 @@ Shows caller ID (if available) instead of the full name.
 The directory that contains the RADIUS configuration files. Defaults to
 \fI/etc/raddb\fP.
 .IP \-F\ \fIradutmp_file\fP
-The file that contains the radutmp file.  If this is specified, -d is
+The file that contains the radutmp file.  If this is specified, \-d is
 not necessary.
 .IP \-i
 Shows the session ID instead of the full name.
@@ -80,7 +80,7 @@ will result in all an Accounting-Request packet being sent to the
 RADIUS server, which tells the server that the NAS rebooted.  i.e. It
 "zaps" all of the users on that NAS.
 
-To "zap" one user, specifiy NAS, username, and NAS port:
+To "zap" one user, specify NAS, username, and NAS port:
 .RS
 .sp
 .nf
index a6a5823..68a33f4 100644 (file)
@@ -14,7 +14,7 @@ Returns: 0 = no duplicate, 1 = duplicate, >1 = error.
 .SH OPTIONS
 
 .IP -d
-Enable printing of debugging informations.
+Enable printing of debugging information.
 
 .IP nas-type
 Type of port/NAS. Can be one of:
index d685081..7b3b86d 100644 (file)
@@ -10,7 +10,7 @@
 .RE
 .sp
 ..
-.TH dictionary 5 "16 Mar 2011"
+.TH dictionary 5 "12 Jun 2015"
 .SH NAME
 dictionary \- RADIUS dictionary file
 .SH DESCRIPTION
@@ -141,9 +141,6 @@ digit) are 1, 2, and 4.  The support values for the number of length
 octets (i.e. the second digit) are 0, 1, and 2.  Any combination of
 those values will work.
 
-You can also use "format=Extended-Vendor-Specific-1", through
-"format=Extended-Vendor-Specific-6".  These define VSAs in the
-"extended vendor-specific" space.
 .TP 0.5i
 .B BEGIN-VENDOR vendor-name
 Define the start of a block of Vendor-Specific attributes.  All of the
@@ -152,6 +149,12 @@ named vendor, until the block is closed by an "END-VENDOR" statement.
 
 This practice is preferred to placing the vendor name at the end of an
 \fIATTRIBUTE\fP  definition.
+
+For VSAs in the RFC 6929 "Extended vendor-specific" space, a format
+can be specified following the "vendor-name".  The format should be
+"format=Extended-Vendor-Specific-1", through
+"format=Extended-Vendor-Specific-6".  The matching "END-VENDOR" should
+just have the "vendor-name", without the format string.
 .TP 0.5i
 .B END-VENDOR vendor-name
 End a previously defined BEGIN-VENDOR block.  The "vendor-name" must match.
@@ -179,3 +182,4 @@ the attribute number should be used instead.
 .BR RFC2865,
 .BR RFC2866,
 .BR RFC2868
+.BR RFC6929
index 3b3acaf..5fb38bf 100644 (file)
@@ -12,7 +12,7 @@
 ..
 .TH radrelay.conf 5 "27 May 2005" "" "FreeRADIUS configuration file"
 .SH NAME
-radrelay.conf \- configuration file for the FreeRADIUS server "radrelay" personality
+radrelay.conf - configuration file for the FreeRADIUS server "radrelay" personality
 .SH DESCRIPTION
 The \fBradrelay.conf\fP file resides in the radius database directory,
 by default \fB/etc/raddb\fP.  It defines the global configuration for
@@ -41,7 +41,7 @@ another, so they both have the same set of accounting data.
 .SH "BUFFERING FOR HIGH-LOAD SERVERS"
 If the RADIUS server suddenly receives a many accounting packets,
 there may be insufficient CPU power to process them all in a timely
-manner.  This problem is especially noticable when the accounting
+manner.  This problem is especially noticeable when the accounting
 packets are going to a back-end database.
 
 Similarly, you may have one database that tracks "live" sessions, and
@@ -123,7 +123,7 @@ to another RADIUS server.
 
 Then, start the server via the following command:
 
-$ radiusd -n radrelay
+$ radiusd \-n radrelay
 
 The server should start up, read the detail file, and process
 accounting packets from it.
index 1c567b8..adb6130 100644 (file)
@@ -97,7 +97,7 @@ Regular Expression Equal
 .B    !~  
 Regular Expression Not Equal
 .PP
-See the default \fI/etc/raddb/attrs\fP for working examples of
+See the default \fI/etc/raddb/mods-config/attr_filter/\fP for working examples of
 sample rule ordering and how to use the different operators.
 .DE
 .PP
@@ -135,7 +135,7 @@ Filters Access-Accept or Access-Reject packets.
 .PP
 .SH FILES
 .I /etc/raddb/radiusd.conf
-.I /etc/raddb/filter/*
+.I /etc/raddb/mods-config/attr_filter/*
 .PP
 .SH "SEE ALSO"
 .BR radiusd (8),
index e89bc9e..c890a11 100644 (file)
@@ -33,12 +33,9 @@ FreeRADIUS variables to create a dynamic filename.
      This accomplishes 'file rotation' automatically from 
      within the server.
 .PP
-.IP perm
+.IP permissions
 The file permissions of the file.  
 If omitted, the default is 0600.
-.IP dirperm
-The directory permissions of the directory where the detail files are
-created.  The default is 0755.
 .IP locking
 This option is set to 'yes' or 'no'.  By default it is 'no'.  Set this
 to yes to enable file locking, which is used with the 'radrelay'
index 792d70c..fb99e0f 100644 (file)
@@ -40,7 +40,7 @@ Once the server has been started (debugging mode is recommended),
 use '\fIradclient\fP to send the following packet to the server:
 .PP
 .DS
-$  radclient -f digest localhost auth testing123
+$  radclient \-f digest localhost auth testing123
 .DE
 
 Where 'digest' is a file containing:
index 6c2af58..5a9ac7b 100644 (file)
@@ -42,7 +42,7 @@ The path to the file.
 .IP format
 The format of the fields in the file, given as an example line from
 the file, with the content of the fields as the RADIUS attributes
-which the fields map to. The fields are seperated by the ':' character
+which the fields map to. The fields are separated by the ':' character
 in the configuration (no matter what is configured for the 'delimiter'
 option).
 .IP hash_size
@@ -75,7 +75,7 @@ added as a control attribute list.
 .PP
 To add an attribute to the RADIUS request (as though it had been sent
 by the NAS), prefix the attribute name in the "format" string with the
-'~' character.
+\(aq~' character.
 .PP
 To add an attribute to the RADIUS reply (to be sent back to the NAS),
 prefix the attribute name in the "format" string with the '='
@@ -98,7 +98,7 @@ format = "My-Group:::*,User-Name"
 .IP
 Parse a file similar to the /etc/group file.  An entry matches a
 request when the name in a User-Name attribute exists in the
-comma-seperated list of a line in the file.  When an entry matches, 
+comma-separated list of a line in the file.  When an entry matches, 
 a "My-Group" attribute will be created and added to the control
 items for the request.  The value of that attribute will be taken from
 the first field of the matching line in the file.
index f1fef26..68fbee3 100644 (file)
@@ -218,7 +218,7 @@ See raddb/sites-available/originate-coa for additional information.
 
 The "session-state" list is primarily used for EAP.  Attributes put
 into the "session-state" list are saved for the next packet in the
-session.  They are automatically retreived when the next packet is
+session.  They are automatically retrieved when the next packet is
 received.
 
 The only contents permitted in an "update" section are attributes and
@@ -348,9 +348,6 @@ 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.
 
-If '#' is used in the NUM portion, it evaluates to the number of times
-the attribute appears in the request.
-
 If '*' is used in the NUM portion, it evaluates to all instances of
 the attribute in the request.
 
@@ -447,7 +444,7 @@ true.  Valid comparison operators are "==", "!=", "<", "<=", ">",
 ">=", "=~", and "!~", all with their usual meanings.  Invalid
 comparison operators are ":=" and "=".
 .RE
-.IP Attribute Comparisons
+.IP "Attribute Comparisons"
 .DS
        (&User-Name == "foo")
 .DE
@@ -457,7 +454,7 @@ evaluates to true if the comparison holds true.  The comparison is
 done by printing the attribute to a string, and then doing a string
 comparison of the two sides of the condition.
 .RE
-.IP Inter-Attribute Comparisons
+.IP "Inter-Attribute Comparisons"
 .DS
        (&User-Name == &Filter-Id)
 .DE
@@ -686,6 +683,27 @@ 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[#]}
+The number of instances of Attribute-Name.
+
+e.g. If a request contains "User-Name = bob", the expansion
+of %{User-Name[#]} will yeild "1".
+
+.IP %{Attribute-Name[*]}
+All values of Attribute-Name, concatenated together with ',' as the
+separator.
+
+.IP %{List-Name:[#]}
+The number of attributes in the named list.
+
+.IP %{List-Name:[*]}
+All values of attributes in the named-list, concatenated together with ','
+as the separator. Use the %{pairs:} xlat to get a list of attributes and
+values.
+
+e.g. If a response contains "Reply-Message = 'Hello', Reply-Message = 'bob'
+the expansion of "%{reply:Reply-Message[*]} will yield "Hello\\nbob"
+
 .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
index c9aef82..deae8a9 100644 (file)
@@ -28,7 +28,7 @@ empty) list of check items, all on one line.  The next line begins
 with a tab, and a (possibly empty) list of reply items.  Each item in
 the check or reply item list is an attribute of the form \fBname =
 value\fP.  Multiple items may be placed on one line, in which case
-they must be seperated by commas.  The reply items may be specified
+they must be separated by commas.  The reply items may be specified
 over multiple lines, in which case each line must end with a comma,
 and the last line of the reply items must not end with a comma.
 
@@ -42,7 +42,7 @@ If the incoming request matches NO entry, then the request is
 rejected.
 
 .SH CAVEATS
-The special username \fBDEFAULT\fP matches any usernames.
+The special keyword \fBDEFAULT\fP matches any usernames.
 
 The entries are processed in order, from the top of the \fBusers\fP file,
 on down.  If an entry contains the special item \fBFall-Through =
@@ -143,22 +143,6 @@ a value less than, or equal to the one given.
 Not allowed as a reply item.
 
 .TP 0.5i
-.B "Attribute =~ Expression"
-As a check item, it matches if the request contains an attribute which
-matches the given regular expression.  This operator may only be
-applied to string attributes.
-.br
-Not allowed as a reply item.
-
-.TP 0.5i
-.B "Attribute !~ Expression"
-As a check item, it matches if the request contains an attribute which
-does not match the given regular expression.  This operator may only be
-applied to string attributes.
-.br
-Not allowed as a reply item.
-
-.TP 0.5i
 .B "Attribute =* Value"
 As a check item, it matches if the request contains the named
 attribute, no matter what the value is.
index 916a8b3..08336c6 100644 (file)
@@ -22,13 +22,13 @@ will be 0 (Note this is the opposite of a normal successful shell status).
 
 .SH OPTIONS
 
-.IP "-d --des"
+.IP "\-d --des"
 Use a DES (Data Encryption Standard) hash (default).
 Ignored if performing a password check.
-.IP "-m --md5"
+.IP "\-m --md5"
 Use a MD5 (Message Digest 5) hash.
 Ignored if performing a password check.
-.IP "-c --check"
+.IP "\-c --check"
 Perform a validation check on a password hash to verify if it matches
 the plantext password.
 
@@ -36,7 +36,7 @@ the plantext password.
 .nf
 $ radcrypt foobar
 HaX0xn7Qy650Q
-$ radcrypt -c foobar HaX0xn7Qy650Q
+$ radcrypt \-c foobar HaX0xn7Qy650Q
 Password OK
 .fi
 .SH SEE ALSO
index 7424e88..c825f22 100644 (file)
@@ -45,11 +45,11 @@ server will exit with a zero status code.
 Note that there are limitations to this check.  Due to the
 complexities involved in \fIalmost\fP starting a RADIUS server, these
 checks are necessarily incomplete.  The server can return a zero
-status code when run with -C, but may still exit with an error when
+status code when run with \-C, but may still exit with an error when
 run normally.
 
 See the output of 
-.B "radiusd -XC"
+.B "radiusd \-XC"
 for an informative list of which modules are checked for correct
 configuration, and which modules are skipped, and therefore not checked.
 .IP "\-d \fIconfig directory\fP"
@@ -96,13 +96,13 @@ Do not spawn threads.
 .IP \-v
 Print server version information and exit.
 .IP \-X
-Debugging mode.  Equivalent to "-sfxx -l stdout".  When trying to
-understand how the server works, ALWAYS run it with "radiusd -X".
+Debugging mode.  Equivalent to "\-sfxx \-l stdout".  When trying to
+understand how the server works, ALWAYS run it with "radiusd \-X".
 For production servers, use "raddebug"
 .IP \-x
 Finer-grained debug mode. In this mode the server will print details
 of every request on it's \fBstdout\fP output. You can specify this
-option multiple times (-x -x or -xx) to get more detailed output.
+option multiple times (\-x \-x or \-xx) to get more detailed output.
 .SH DEBUGGING
 The default configuration is set to work in the widest possible
 circumstances.  It requires minimal changes for your system.
@@ -113,7 +113,7 @@ guaranteed method of failure.  Instead, we STRONGLY RECOMMEND
 proceeding via the following steps:
 .PP
 1) Always run the server in debugging mode (
-.B radiusd -X
+.B radiusd \-X
 ) after making a configuration change.  We cannot emphasize this
 enough.  If you are not running the server in debugging mode, you
 \fIwill not\fP be able to see what is doing, and you \fIwill not\fP be
@@ -165,14 +165,14 @@ can be skipped.
 .br
 .br
 d) Start the server in debugging mode (
-.B radiusd -X
+.B radiusd \-X
 ), and start testing.
 .in -0.3i
 .PP
 5) Ask questions on the mailing list
 (freeradius-users@lists.freeradius.org).  When asking questions,
 include the output from debugging mode (
-.B radiusd -X
+.B radiusd \-X
 ).  This information will allow people to help you.  If you do not
 include it, the first response to your message will be "post the
 output of debug mode".
@@ -191,7 +191,7 @@ the case of a PPP connection.
 .PP
 The access server also sends login and logout records to the \fBradius\fP
 server so accounting can be done. These records are kept for each terminal
-server seperately in a file called \fBdetail\fP, and in the \fIwtmp\fP
+server separately in a file called \fBdetail\fP, and in the \fIwtmp\fP
 compatible logfile \fB/var/log/radwtmp\fP.
 .SH CONFIGURATION
 \fBRadiusd\fP uses a number of configuration files. Each file has it's
index 577c2b6..5ecc963 100644 (file)
@@ -77,7 +77,7 @@ enclosed in double-quotes must have back-slashes and the quotation
 marks escaped inside of the string.
 
 Only one debug condition can be active at a time.
-.IP debug\ condition\ '((User-Name\ ==\ "bob")\ ||\ (Packet-Src-IP-Address\ ==\ 192.0.2.22))'
+.IP "debug condition '((User-Name == ""bob"") || (Packet-Src-IP-Address == 192.0.2.22))'"
 A more complex condition that enables debugging output for requests
 containing User-Name "bob", or requests that originate from source IP
 address 192.0.2.22.
index a8694c6..fdba699 100644 (file)
@@ -30,7 +30,7 @@ See raddb/sites-available/buffered-sql for more information.
 .SH "BUFFERING FOR HIGH-LOAD SERVERS"
 If the RADIUS server suddenly receives a many accounting packets,
 there may be insufficient CPU power to process them all in a timely
-manner.  This problem is especially noticable when the accounting
+manner.  This problem is especially noticeable when the accounting
 packets are going to a back-end database.
 
 Similarly, you may have one database that tracks "live" sessions, and
index db1eaa1..ebc1896 100644 (file)
@@ -19,7 +19,9 @@ radsniff - dump radius protocol
 .RB [ \-p
 .IR port ]
 .RB [ \-r
-.IR filter ]
+.IR request filter]
+.RB [ \-R
+.IR response filter ]
 .RB [ \-s
 .IR secret ]
 .RB [ \-S ]
@@ -52,8 +54,10 @@ Read packets from filename.
 Print packet headers only, not contents.
 .IP \-p\ \fIport\fP
 \tListen for packets on port.
-.IP \-r\ \fIfilter\fP
-RADIUS attribute filter.
+.IP \-r\ \fIresponse-filter\fP
+RADIUS attribute request filter.
+.IP \-R\ \fIrequest-filter\fP
+RADIUS attribute response filter.
 .IP \-s\ \fIsecret\fP
 RADIUS secret.
 .IP \-S
index 65622e1..344c3ab 100644 (file)
@@ -71,7 +71,7 @@ The pathname of the SQL logfile to use.
 
 .SH NOTES
 .SS Oracle driver
-The command "radsqlrelay -d oracle -b db.domain.tld sql-relay" reads the
+The command "radsqlrelay \-d oracle \-b db.domain.tld sql-relay" reads the
 database description stored in $TNS_ADMIN/tnsnames.ora:
 .PP
 .DS
diff --git a/mibs/RADIUS-ACC-SERVER-MIB.chart b/mibs/RADIUS-ACC-SERVER-MIB.chart
deleted file mode 100644 (file)
index 6c26286..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-+-(67) radiusMIB
-  +-(2) radiusAccounting
-    +-(1) radiusAccServMIB 
-      +-(1) radiusAccServMIBObjects
-      | +-(1) radiusAccServ
-      |   +-(1) radiusAccServIdent
-      |   +-(2) radiusAccServUpTime
-      |   +-(3) radiusAccServResetTime
-      |   +-(4) radiusAccServConfigReset     *
-      |   +-(5) radiusAccServTotalRequests
-      |   +-(6) radiusAccServTotalInvalidRequests
-      |   +-(7) radiusAccServTotalDupRequests
-      |   +-(8) radiusAccServTotalResponses
-      |   +-(9) radiusAccServTotalMalformedRequests
-      |   +-(10) radiusAccServTotalBadAuthenticators
-      |   +-(11) radiusAccServTotalPacketsDropped
-      |   +-(12) radiusAccServTotalNoRecords
-      |   +-(13) radiusAccServTotalUnknownTypes
-      |   +-(14) radiusAccClientTable                  
-      |     +-(1) radiusAccClientEntry
-      |       +-(1) radiusAccClientIndex
-      |       +-(2) radiusAccClientAddress
-      |       +-(3) radiusAccClientID
-      |       +-(4) radiusAccServPacketsDropped
-      |       +-(5) radiusAccServRequests
-      |       +-(6) radiusAccServDupRequests
-      |       +-(7) radiusAccServResponses
-      |       +-(8) radiusAccServBadAuthenticators
-      |       +-(9) radiusAccServMalformedRequests
-      |       +-(10) radiusAccServNoRecords
-      |       +-(11) radiusAccServUnknownTypes         
-      |
-      +-(2) radiusAccServMIBConformance
-        +-(1) radiusAccServMIBCompliances
-        | +-(1) radiusAccServMIBCompliance
-        |
-        +-(2) radiusAccServMIBGroups
-          +-(1) radiusAccServMIBGroup
-       
-
-
-
-
-
diff --git a/mibs/RADIUS-AUTH-SERVER-MIB.chart b/mibs/RADIUS-AUTH-SERVER-MIB.chart
deleted file mode 100644 (file)
index 4d66d23..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-+-(67) radiusMIB
-  +-(1) radiusAuthentication
-    +-(1) radiusAuthServMIB
-    | +-(1) radiusAuthServMIBObjects
-    |   +-(1) radiusAuthServ
-    |     +-(1) radiusAuthServIdent
-    |     +-(2) radiusAuthServUpTime
-    |     +-(3) radiusAuthServResetTime
-    |     +-(4) radiusAuthServConfigReset 
-    |     +-(5) radiusAuthServTotalAccessRequests
-    |     +-(6) radiusAuthServTotalInvalidRequests
-    |     +-(7) radiusAuthServTotalDupAccessRequests
-    |     +-(8) radiusAuthServTotalAccessAccepts
-    |     +-(9) radiusAuthServTotalAccessRejects
-    |     +-(10) radiusAuthServTotalAccessChallenges
-    |     +-(11) radiusAuthServTotalMalformedAccessRequests
-    |    +-(12) radiusAuthServTotalBadAuthenticators
-    |     +-(13) radiusAuthServTotalPacketsDropped
-    |     +-(14) radiusAuthServTotalUnknownTypes
-    |     +-(15) radiusAuthClientTable
-    |       +-(1) radiusAuthClientEntry
-    |         +-(1) radiusAuthClientIndex
-    |         +-(2) radiusAuthClientAddress
-    |         +-(3) radiusAuthClientID
-    |         +-(4) radiusAuthServAccessRequests
-    |         +-(5) radiusAuthServDupAccessRequests
-    |         +-(6) radiusAuthServAccessAccepts
-    |         +-(7) radiusAuthServAccessRejects
-    |         +-(8) radiusAuthServAccessChallenges
-    |         +-(9) radiusAuthServMalformedAccessRequests
-    |         +-(10) radiusAuthServBadAuthenticators
-    |         +-(11) radiusAuthServPacketsDropped
-    |         +-(12) radiusAuthServUnknownTypes
-    +-(2) radiusAuthServMIBConformance
-      +-(1) radiusAuthServMIBCompliances
-        +-(1) radiusAuthServMIBCompliance
-      +-(2) radiusAuthServMIBGroups
-        +-(1) radiusAuthServMIBGroup
-
-
-
-
-
index 9b26a5f..80eaa6d 100644 (file)
@@ -4,7 +4,4 @@ RADIUS-ACC-SERVER-MIB.txt and RADIUS-AUTH-SERVER-MIB.txt are
 extracted from RFCs 2621 and 2619 accordingly. The corresponding
 RFCs can be found in subdirectory doc/rfc of the package. Both
 RFCs allow unlimited distribution of the information contained
-therein.
-
-*.chart files contain the graphic representation of the corresponding
-MIB trees.
+therein.
\ No newline at end of file
index 2fa7639..c186ab0 100644 (file)
@@ -9,7 +9,7 @@
 #
 ######################################################################
 
-DH_KEY_SIZE    = 1024
+DH_KEY_SIZE    = 2048
 
 #
 #  Set the passwords
@@ -22,7 +22,7 @@ DH_KEY_SIZE   = 1024
 #
 ######################################################################
 .PHONY: all
-all: index.txt serial dh random server ca client
+all: index.txt serial dh server ca client
 
 .PHONY: client
 client: client.pem
@@ -46,7 +46,7 @@ passwords.mk: server.cnf ca.cnf client.cnf
 #
 ######################################################################
 dh:
-       openssl dhparam -out dh $(DH_KEY_SIZE)
+       openssl gendh -out dh -2 $(DH_KEY_SIZE)
 
 ######################################################################
 #
@@ -118,13 +118,6 @@ index.txt:
 serial:
        @echo '01' > serial
 
-random:
-       @if [ -c /dev/urandom ] ; then \
-               ln -sf /dev/urandom random; \
-       else \
-               date > ./random; \
-       fi
-
 print:
        openssl x509 -text -in server.crt
 
@@ -139,4 +132,4 @@ clean:
 #
 destroycerts:
        rm -f *~ dh *.csr *.crt *.p12 *.der *.pem *.key index.txt* \
-                       serial* random *\.0 *\.1
+                       serial*  *\.0 *\.1
index b7352dc..d49991b 100644 (file)
@@ -19,7 +19,7 @@ default_crl_days      = 30
 default_md             = sha256
 preserve               = no
 policy                 = policy_match
-crlDistributionPoints  = URI:http://www.example.com/example_ca.crl
+crlDistributionPoints  = URI:http://www.example.org/example_ca.crl
 
 [ policy_match ]
 countryName            = match
@@ -51,12 +51,12 @@ countryName         = FR
 stateOrProvinceName    = Radius
 localityName           = Somewhere
 organizationName       = Example Inc.
-emailAddress           = admin@example.com
+emailAddress           = admin@example.org
 commonName             = "Example Certificate Authority"
 
 [v3_ca]
 subjectKeyIdentifier   = hash
 authorityKeyIdentifier = keyid:always,issuer:always
-basicConstraints       = CA:true
-crlDistributionPoints  = URI:http://www.example.com/example_ca.crl
+basicConstraints       = critical,CA:true
+crlDistributionPoints  = URI:http://www.example.org/example_ca.crl
 
index 9627c67..2650e47 100644 (file)
@@ -49,5 +49,5 @@ countryName           = FR
 stateOrProvinceName    = Radius
 localityName           = Somewhere
 organizationName       = Example Inc.
-emailAddress           = user@example.com
-commonName             = user@example.com
+emailAddress           = user@example.org
+commonName             = user@example.org
index 48af1f2..025ebb5 100644 (file)
@@ -49,6 +49,6 @@ countryName           = FR
 stateOrProvinceName    = Radius
 localityName           = Somewhere
 organizationName       = Example Inc.
-emailAddress           = admin@example.com
+emailAddress           = admin@example.org
 commonName             = "Example Server Certificate"
 
index 7ac69ff..624bc09 100644 (file)
@@ -30,30 +30,29 @@ cache {
        #                          a cluster of RADIUS servers.
 #      driver = "rlm_cache_rbtree"
 
-#
-#      Some drivers accept specific options, to set them a
-#      config section with the the name as the driver should be added
-#      to the cache instance.
-#
-#      Driver specific options are:
-#
+       #
+       #  Some drivers accept specific options, to set them a
+       #  config section with the the name as the driver should be added
+       #  to the cache instance.
+       #
+       #  Driver specific options are:
+       #
 #      memcached {
 #              # Memcached configuration options, as documented here:
 #              #    http://docs.libmemcached.org/libmemcached_configuration.html#memcached
 #              options = "--SERVER=localhost"
 #
 #              pool {
-#                      start = 5
-#                      min = 4
+#                      start = ${thread[pool].start_servers}
+#                      min = ${thread[pool].min_spare_servers}
 #                      max = ${thread[pool].max_servers}
-#                      spare = 3
+#                      spare = ${thread[pool].max_spare_servers}
 #                      uses = 0
 #                      lifetime = 0
 #                      idle_timeout = 60
 #              }
 #      }
 
-
        #  The key used to index the cache.  It is dynamically expanded
        #  at run time.
        key = "%{User-Name}"
@@ -91,8 +90,12 @@ cache {
        #  represents the cache entry. see man unlang for more information
        #  on update blocks.
        #
+       #  Note: Only request, reply, control and session-state lists
+       #  are available in cache entries. Attempting to store attributes
+       #  in other lists will raise an error during config validation.
+       #
        update {
-               # [outer.]<list>:<attribute> <op> <value>
+               # <list>:<attribute> <op> <value>
 
                # Cache all instances of Reply-Message in the reply list
                reply:Reply-Message += &reply:Reply-Message
index e9a3aed..376fc5b 100644 (file)
@@ -8,6 +8,6 @@ cache cache_eap {
 
        update reply {
                reply: += &reply:
-               control:State := &request:State
+               &control:State := &request:State
        }
 }
index aefb310..6eff33b 100644 (file)
@@ -25,39 +25,39 @@ couchbase {
        # Map attribute names to json element names for accounting.
        #
        # Configuration items are in the format:
-       #  <element name> = '<radius attribute>'
+       #  <radius attribute> = '<element name>'
        #
-       # Attribute names should be single quoted.
+       # Element 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'
-               framedProtocol      = 'Framed-Protocol'
-               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'
+       update {
+               Acct-Session-Id         = 'sessionId'
+               Acct-Unique-Session-Id  = 'uniqueId'
+               Acct-Status-Type        = 'lastStatus'
+               Acct-Authentic          = 'authentic'
+               User-Name               = 'userName'
+               Stripped-User-Name      = 'strippedUserName'
+               Stripped-User-Domain    = 'strippedUserDomain'
+               Realm                   = 'realm'
+               NAS-IP-Address          = 'nasIpAddress'
+               NAS-Identifier          = 'nasIdentifier'
+               NAS-Port                = 'nasPort'
+               Called-Station-Id       = 'calledStationId'
+               Called-Station-SSID     = 'calledStationSSID'
+               Calling-Station-Id      = 'callingStationId'
+               Framed-Protocol         = 'framedProtocol'
+               Framed-IP-Address       = 'framedIpAddress'
+               NAS-Port-Type           = 'nasPortType'
+               Connect-Info            = 'connectInfo'
+               Acct-Session-Time       = 'sessionTime'
+               Acct-Input-Packets      = 'inputPackets'
+               Acct-Output-Packets     = 'outputPackets'
+               Acct-Input-Octets       = 'inputOctets'
+               Acct-Output-Octets      = 'outputOctets'
+               Acct-Input-Gigawords    = 'inputGigawords'
+               Acct-Output-Gigawords   = 'outputGigawords'
+               Event-Timestamp         = 'lastUpdated'
        }
 
        # Couchbase document key for user documents (unlang supported)
@@ -77,6 +77,22 @@ couchbase {
                view = "_design/client/_view/by_id"
 
                #
+               #  Sets default values (not obtained from couchbase) for new client entries
+               #
+               template {
+#                      login                           = 'test'
+#                      password                        = 'test'
+#                      proto                           = tcp
+#                      require_message_authenticator   = yes
+
+                       # Uncomment to add a home_server with the same
+                       # attributes as the client.
+#                      coa_server {
+#                              response_window = 2.0
+#                      }
+               }
+
+               #
                # Client mappings are in the format:
                #  <client attribute> = '<element name>'
                #
@@ -107,74 +123,83 @@ couchbase {
        # Set to 'yes' to enable simultaneous use checking (multiple logins).
        # NOTE: This will cause the execution of a view request on every check
        # and may be a performance penalty.
-       #check_simul = no
+#      check_simul = no
 
        # Couchbase view that should return all account documents keyed by username.
-       #simul_view = "_design/acct/_view/by_user"
+#      simul_view = "_design/acct/_view/by_user"
 
        # The key to the above view.
        # NOTE: This will need to match EXACTLY what you emit from your view.
-       #simul_vkey = "%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}"
+#      simul_vkey = "%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}"
 
        # Set to 'yes' to enable verification of the results returned from the above view.
        # NOTE: This may be an additional performance penalty to the actual check and
        # should be avoided unless absolutely neccessary.
-       #verify_simul = no
+#      verify_simul = no
 
        # Remove stale session if checkrad does not see a double login.
        # NOTE: This will only be executed if both check_simul and verify_simul
        # are set to 'yes' above.
-       #delete_stale_sessions = yes
+#      delete_stale_sessions = yes
 
        #
        #  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
+               #  Connections to create during module instantiation.
+               #  If the server cannot create specified number of
+               #  connections during instantiation it will exit.
+               #  Set to 0 to allow the server to start without the
+               #  couchbase being available.
                start = ${thread[pool].start_servers}
 
-               # Minimum number of connections to keep open
+               #  Minimum number of connections to keep open
                min = ${thread[pool].min_spare_servers}
 
-               # 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.
                #
-               # 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'
                #
-               # NOTE: This should be greater than or equal to "min" above.
+               #  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.  This should be less than or equal to "max" above.
+               #  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
+               #  Number of uses before the connection is closed
                #
-               # NOTE: A setting of 0 means infinite (no limit).
+               #  0 means "infinite"
                uses = 0
 
-               # The lifetime (in seconds) of the connection
+               #  The lifetime (in seconds) of the connection
                #
-               # NOTE: A setting of 0 means infinite (no limit).
+               #  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.
+               #  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).
+               #  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.
+               #  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 db03a52..e91e12d 100644 (file)
@@ -16,8 +16,8 @@ detail {
        #  day, so that the detail file doesn't have to go
        #  through a 'log rotation'
        #
-       #  If your detail files are large, you may also want
-       #  to add a ':%H' (see doc/variables.txt) to the end
+       #  If your detail files are large, you may also want to add
+       #  a ':%H' (see doc/configuration/variables.rst) to the end
        #  of it, to create a new detail file every hour, e.g.:
        #
        #   ..../detail-%Y%m%d:%H
@@ -68,8 +68,8 @@ detail {
        #  format (see "man ctime" for details).
        #
        #  The header can be customised by editing this
-       #  string.  See "doc/variables.txt" for a description
-       #  of what can be put here.
+       #  string.  See "doc/configuration/variables.rst" for a
+       #  description of what can be put here.
        #
        header = "%t"
 
index 0a014ec..7739a60 100644 (file)
@@ -1,8 +1,16 @@
-##  Configuration for DHCP to use SQL IP Pools.
-##
-##  See sqlippool.conf for common configuration explanation
-##
-##  $Id$
+#  Configuration for DHCP to use SQL IP Pools.
+#
+#  See raddb/mods-available/sqlippool for common configuration explanation
+#
+#  See raddb/policy.d/dhcp_sqlippool for the "glue" code that allows
+#  the RADIUS based "sqlippool" module to be used for DHCP.
+#
+#  See raddb/mods-config/sql/ippool/ for the schemas.
+#
+#  See raddb/sites-available/dhcp for instructions on how to configure
+#  the DHCP server.
+#
+#  $Id$
 
 sqlippool dhcp_sqlippool {
        sql_module_instance = "sql"
index db1356f..8759784 100644 (file)
@@ -241,7 +241,7 @@ eap {
                #  write to files in its configuration
                #  directory.
                #
-#              random_file = ${certdir}/random
+       #       random_file = /dev/urandom
 
                #
                #  This can never exceed the size of a RADIUS
@@ -269,9 +269,13 @@ eap {
                #  1) Copy CA certificates and CRLs to same directory.
                #  2) Execute 'c_rehash <CA certs&CRLs Directory>'.
                #    'c_rehash' is OpenSSL's command.
-               #  3) uncomment the line below.
+               #  3) uncomment the lines below.
                #  5) Restart radiusd
        #       check_crl = yes
+
+               # Check if intermediate CAs have been revoked.
+       #       check_all_crl = yes
+
                ca_path = ${cadir}
 
                #
@@ -348,26 +352,32 @@ eap {
                #
                cache {
                        #
-                       #  Enable it.  The default is "no".
-                       #  Deleting the entire "cache" subsection
-                       #  also disables caching.
+                       #  Enable it.  The default is "no". Deleting the entire "cache"
+                       #  subsection also disables caching.
                        #
-                       #  You can disallow resumption for a
-                       #  particular user by adding the following
-                       #  attribute to the control item list:
+                       #  You can disallow resumption for a particular user by adding the
+                       #  following attribute to the control item list:
                        #
-                       #               Allow-Session-Resumption = No
+                       #    Allow-Session-Resumption = No
                        #
-                       #  If "enable = no" below, you CANNOT
-                       #  enable resumption for just one user
-                       #  by setting the above attribute to "yes".
+                       #  If "enable = no" below, you CANNOT enable resumption for just one
+                       #  user by setting the above attribute to "yes".
                        #
                        enable = yes
 
                        #
-                       #  Lifetime of the cached entries, in hours.
-                       #  The sessions will be deleted after this
-                       #  time.
+                       #  Internal "name" of the session cache. Used to distinguish which
+                       #  TLS context sessions belong to.
+                       #
+                       #  The server will generate a random value if unset. This will change
+                       #  across server restart so you MUST set the "name" if you want to
+                       #  persist sessions (see below).
+                       #
+               #       name = "EAP module"
+
+                       #
+                       #  Lifetime of the cached entries, in hours. The sessions will be
+                       #  deleted/invalidated after this time.
                        #
                        lifetime = 24 # hours
 
@@ -792,5 +802,11 @@ eap {
                #  working.
                #
 #              send_error = no
+
+               #  Server identifier to send back in the challenge.
+               #  This should generally be the host name of the
+               #  RADIUS server.  Or, some information to uniquely
+               #  identify it.
+#              identity = "FreeRADIUS"
        }
 }
index 5f21e43..bb1d437 100644 (file)
@@ -17,7 +17,7 @@
 #
 #  The RADIUS attributes from the user request will be placed
 #  into environment variables of the executed program, as
-#  described in "man unlang" and in doc/variables.txt
+#  described in "man unlang" and in doc/configuration/variables.rst
 #
 #  See also "echo" for more sample configuration.
 #
@@ -25,6 +25,5 @@ exec {
        wait = no
        input_pairs = request
        shell_escape = yes
-       output = none
        timeout = 10
 }
index dfc6ca8..663a9f4 100644 (file)
 
 #
 #  The module also registers a few paircompare functions, and
-#  many string manipulation functions, which should really be
-#  documented here.
+#  many string manipulation functions, including:
+#
+#  rand                get random number from 0 to n-1
+#              "%{rand:10}" == "9"
+#
+#  randstr     get random string built from character classes:
+#                      c lowercase letters
+#                      C uppercase letters
+#                      n numbers
+#                      a alphanumeric
+#                      ! punctuation
+#                      . alphanumeric + punctuation
+#                      s alphanumeric + "./"
+#                      o characters suitable for OTP (easily confused removed)
+#                      h binary data as lowercase hex
+#                      H binary data as uppercase hex
+#
+#              "%{randstr:CCCC!!cccnnn}" == "IPFL>{saf874"
+#              "%{randstr:oooooooo}" == "rfVzyA4y"
+#              "%{randstr:hhhh}" == "68d60de3"
+#
+#  urlquote    quote special characters in URI
+#              "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
+#
+#  urlunquote  unquote URL special characters
+#              "%{urlunquote:http%%3A%%47%%47example.org%%47}" == "http://example.org/"
+#
+#  escape      escape string similar to rlm_sql safe_characters
+#              "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" 
+#
+#  unescape    reverse of escape
+#              "%{unescape:=60img=62foo.jpg=60/img=62}" == "<img>foo.jpg</img>"
+#
+#  tolower     convert to lowercase
+#              "%{tolower:Bar}" == "bar"
+#
+#  toupper     convert to uppercase
+#              "%{toupper:Foo}" == "FOO"
+#
+#  md5         get md5sum hash
+#              "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
+#              
+#  sha1                get sha1 hash
+#              "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+#
+#  sha256      get sha256 hash
+#              "%{sha256:foo}" == "2c26b46b68ffc68ff99b453c1d30413413422d706..."
+#
+#  sha512      get sha512 hash
+#              "%{sha512:foo}" == "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae29838..."
+#
+#  hmacmd5     generate HMAC-MD5 of string
+#              "%{hmacmd5:foo bar}" == "31b6db9e5eb4addb42f1a6ca07367adc"
+#
+#  hmacsha1    generate HMAC-SHA1 of string
+#              "%{hmacsha1:foo bar}" == "85d155c55ed286a300bd1cf124de08d87e914f3a"
+#
+#  pairs       serialize attributes as comma-delimited string
+#              "%{pairs:request:}" == "User-Name = 'foo', User-Password = 'bar', ..."
+#
+#  base64      encode string as base64
+#              "%{base64:foo}" == "Zm9v"
+#
+#  base64tohex convert base64 to hex
+#              "%{base64tohex:Zm9v}" == "666f6f"
+#
+#  explode     split an attribute into multiple new attributes based on a delimiter
+#              "%{explode:&ref <delim>}"
+#
+#  nexttime    calculate number of seconds until next n hour(s), day(s), week(s), year(s)
+#              if it were 16:18, %{nexttime:1h} would expand to 2520
+#
+#  lpad                left-pad a string
+#              if User-Name is "foo": "%{lpad:&User-Name 6 x}" == "xxxfoo"
+#
+#  rpad                right-pad a string
+#              if User-Name is "foo": "%{rpad:&User-Name 5 -}" == "foo--"
 #
 
 expr {
        #
-       # Characters that will not be encoded by the %{encode}
+       # Characters that will not be encoded by the %{escape}
        # xlat function.
        #
        safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /äéöüàâæçèéêëîïôœùûüaÿÄÉÖÜßÀÂÆÇÈÉÊËÎÏÔŒÙÛÜŸ"
index c14992f..e3f3bf5 100644 (file)
@@ -21,7 +21,7 @@ files {
 
        #  This is accepted for backwards compatibility
        #  It will be removed in a future release.
-       usersfile = ${moddir}/authorize
+#      usersfile = ${moddir}/authorize
 
        #  These are accepted for backwards compatibility.
        #  They will be renamed in a future release.
index 9a690b7..2b4df62 100644 (file)
@@ -79,7 +79,7 @@ eap inner-eap {
 
                #  Other needful things
                dh_file = ${certdir}/dh
-               random_file = ${certdir}/random
+               random_file = /dev/urandom
 
                #  CRL and OCSP things go here.  See the main "eap"
                #  file for details.
index 29a92b9..3b7e16d 100644 (file)
@@ -18,50 +18,59 @@ krb5 {
        #  that it was thread safe at compile time.
        #
        pool {
-               # Number of connections to start
+               #  Connections to create during module instantiation.
+               #  If the server cannot create specified number of
+               #  connections during instantiation it will exit.
+               #  Set to 0 to allow the server to start without the
+               #  KDC being available.
                start = ${thread[pool].start_servers}
 
-               # Minimum number of connections to keep open
+               #  Minimum number of connections to keep open
                min = ${thread[pool].min_spare_servers}
 
-               # 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.
+               #  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.
+               #  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.  This should be less than or equal to "max" above.
+               #  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
+               #  Number of uses before the connection is closed
                #
-               # NOTE: A setting of 0 means infinite (no limit).
+               #  0 means "infinite"
                uses = 0
 
-               # The lifetime (in seconds) of the connection
+               #  The lifetime (in seconds) of the connection
                #
-               # NOTE: A setting of 0 means infinite (no limit).
+               #  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.
+               #  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).
+               #  NOTE: A setting of 0 means infinite (no timeout).
                idle_timeout = 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.
+               #  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 4126944..0a1cf02 100644 (file)
@@ -24,6 +24,7 @@ ldap {
 #      port = 389
 
        #  Administrator account for searching and possibly modifying.
+       #  If using SASL + KRB5 these should be commented out.
 #      identity = 'cn=admin,dc=example,dc=org'
 #      password = mypass
 
@@ -31,9 +32,36 @@ ldap {
        #  searches will start from.
        base_dn = 'dc=example,dc=org'
 
-       #  SASL mechanism to use for administrative binds.
-       #  Uncomment for certificate auth or peercred auth (ldapi:// only).
-#      sasl_mech = 'EXTERNAL'
+       #
+       #  SASL parameters to use for admin binds
+       #
+       #  When we're prompted by the SASL library, these control
+       #  the responses given, as well as the identity and password
+       #  directives above.
+       #
+       #  If any directive is commented out, a NULL response will be
+       #  provided to cyrus-sasl.
+       #
+       #  Unfortunately the only way to control Keberos here is through
+       #  environmental variables, as cyrus-sasl provides no API to
+       #  set the krb5 config directly.
+       #
+       #  Full documentation for MIT krb5 can be found here:
+       #
+       #       http://web.mit.edu/kerberos/krb5-devel/doc/admin/env_variables.html
+       #
+       #  At a minimum you probably want to set KRB5_CLIENT_KTNAME.
+       #
+       sasl {
+               # SASL mechanism
+#              mech = 'PLAIN'
+
+               # SASL authorisation identity to proxy.
+#              proxy = 'autz_id'
+
+               # SASL realm. Used for kerberos.
+#              realm = 'example.org'
+       }
 
        #
        #  Generic valuepair attribute
@@ -92,8 +120,9 @@ ldap {
                #  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'
+               control:                        += 'radiusControlAttribute'
+               request:                        += 'radiusRequestAttribute'
+               reply:                          += 'radiusReplyAttribute'
        }
 
        #  Set to yes if you have eDirectory and want to use the universal
@@ -121,18 +150,47 @@ ldap {
        #
        user {
                #  Where to start searching in the tree for users
-               base_dn = '${..base_dn}'
+               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}})"
 
-               #  SASL mechanism to use for user binds.
-#              sasl_mech = 'PLAIN'
+               #  SASL parameters to use for user binds
+               #
+               #  When we're prompted by the SASL library, these control
+               #  the responses given.
+               #
+               #  Any of the config items below may be an attribute ref
+               #  or and expansion, so different SASL mechs, proxy IDs
+               #  and realms may be used for different users.
+               sasl {
+                       # SASL mechanism
+#                      mech = 'PLAIN'
+
+                       # SASL authorisation identity to proxy.
+#                      proxy = &User-Name
+
+                       # SASL realm. Used for kerberos.
+#                      realm = 'example.org'
+               }
 
                #  Search scope, may be 'base', 'one', sub' or 'children'
 #              scope = 'sub'
 
+               #  Server side result sorting
+               #
+               #  A list of space delimited attributes to order the result
+               #  set by, if the filter matches multiple objects.
+               #  Only the first result in the set will be processed.
+               #
+               #  If the attribute name is prefixed with a hyphen '-' the
+               #  sorting order will be reversed for that attribute.
+               #
+               #  If sort_by is set, and the server does not support sorting
+               #  the search will fail.
+#              sort_by = '-uid'
+
                #  If this is undefined, anyone is authorised.
                #  If it is defined, the contents of this attribute
                #  determine whether or not the user is authorised
@@ -156,7 +214,8 @@ ldap {
                #    access_positive = yes
                #    access_attribute = userAccessAllowed
                #
-               #    userAccessAllowed = false
+               #  With an LDAP object containing:
+               #    userAccessAllowed: false
                #
                #  Will result in the user being locked out.
 #              access_positive = yes
@@ -167,7 +226,7 @@ ldap {
        #
        group {
                #  Where to start searching in the tree for groups
-               base_dn = '${..base_dn}'
+               base_dn = "${..base_dn}"
 
                #  Filter for group objects, should match all available
                #  group objects a user might be a member of.
@@ -211,28 +270,34 @@ ldap {
 #              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.
+               #  Override the normal cache attribute (<inst>-LDAP-Group or
+               #  LDAP-Group if using the default instance) and create a
+               #  custom attribute.  This can help if multiple module instances
+               #  are used in fail-over.
 #              cache_attribute = 'LDAP-Cached-Membership'
        }
 
        #
        #  User profiles. RADIUS profile objects contain sets of attributes
        #  to insert into the request. These attributes are mapped using
-       #  the same mapping scheme applied to user objects.
+       #  the same mapping scheme applied to user objects (the update section above).
        #
        profile {
                #  Filter for RADIUS profile objects
 #              filter = '(objectclass=radiusprofile)'
 
-               #  The default profile applied to all users.
+               #  The default profile.  This may be a DN or an attribute
+               #  reference.
+               #  To get old v2.2.x style behaviour, or to use the
+               #  &User-Profile attribute to specify the default profile,
+               #  set this to &control:User-Profile.
 #              default = 'cn=radprofile,dc=example,dc=org'
 
-               #  The list of profiles which are applied (after the default)
-               #  to all users.
-               #  The 'User-Profile' attribute in the control list
-               #  will override this setting at run-time.
+               #  The LDAP attribute containing profile DNs to apply
+               #  in addition to the default profile above.  These are
+               #  retrieved from the user object, at the same time as the
+               #  attributes from the update section, are are applied
+               #  if authorization is successful.
 #              attribute = 'radiusProfileDn'
        }
 
@@ -241,7 +306,7 @@ ldap {
        #
        client {
                #   Where to start searching in the tree for clients
-               base_dn = '${..base_dn}'
+               base_dn = "${..base_dn}"
 
                #
                #  Filter to match client objects
@@ -252,6 +317,22 @@ ldap {
 #              scope = 'sub'
 
                #
+               #  Sets default values (not obtained from LDAP) for new client entries
+               #
+               template {
+#                      login                           = 'test'
+#                      password                        = 'test'
+#                      proto                           = tcp
+#                      require_message_authenticator   = yes
+
+                       # Uncomment to add a home_server with the same
+                       # attributes as the client.
+#                      coa_server {
+#                              response_window = 2.0
+#                      }
+               }
+
+               #
                #  Client attribute mappings are in the format:
                #      <client attribute> = <ldap attribute>
                #
@@ -354,13 +435,13 @@ ldap {
                rebind = yes
 
                #  Seconds to wait for LDAP query to finish. default: 20
-               timeout = 10
+               res_timeout = 10
 
                #  Seconds LDAP server has to process the query (server-side
                #  time limit). default: 20
                #
                #  LDAP_OPT_TIMELIMIT is set to this value.
-               timelimit = 3
+               srv_timelimit = 3
 
                #  Seconds to wait for response of the server. (network
                #  failures) default: 10
@@ -410,19 +491,22 @@ ldap {
 #              ca_path = ${certdir}
 #              certificate_file = /path/to/radius.crt
 #              private_key_file = /path/to/radius.key
-#              random_file = ${certdir}/random
+#              random_file = /dev/urandom
+
+               #  Certificate Verification requirements.  Can be:
+               #    'never' (do not even bother trying)
+               #    'allow' (try, but don't fail if the certificate
+               #               cannot be verified)
+               #    'demand' (fail if the certificate does not verify)
+               #    'hard'  (similar to 'demand' but fails if TLS
+               #             cannot negotiate)
+               #
+               #  The default is libldap's default, which varies based
+               #  on the contents of ldap.conf.
 
-               #  Certificate Verification requirements.  Can be:
-               #    'never' (don't even bother trying)
-               #    'allow' (try, but don't fail if the certificate
-               #               can't be verified)
-               #    'demand' (fail if the certificate doesn't verify.)
-               #
-               #  The default is 'allow'
 #              require_cert    = 'demand'
        }
 
-
        #  As of version 3.0, the 'pool' section has replaced the
        #  following configuration items:
        #
@@ -434,11 +518,15 @@ ldap {
        #  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
+               #  Connections to create during module instantiation.
+               #  If the server cannot create specified number of
+               #  connections during instantiation it will exit.
+               #  Set to 0 to allow the server to start without the
+               #  directory being available.
+               start = ${thread[pool].start_servers}
 
                #  Minimum number of connections to keep open
-               min = 4
+               min = ${thread[pool].min_spare_servers}
 
                #  Maximum number of connections
                #
@@ -455,15 +543,20 @@ ldap {
 
                #  Spare connections to be left idle
                #
-               #  NOTE: Idle connections 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 connection is closed
                #
-               #  0 means 'infinite'
+               #  0 means "infinite"
                uses = 0
 
+               #  The number of seconds to wait after the server tries
+               #  to open a connection, and fails.  During this time,
+               #  no new connections will be opened.
+               retry_delay = 30
+
                #  The lifetime (in seconds) of the connection
                lifetime = 0
 
@@ -471,12 +564,6 @@ ldap {
                #  unused for this length of time will be closed.
                idle_timeout = 60
 
-               # The number of seconds to wait after the server tries
-               # to open a connection, and fails.  During this time,
-               # no new connections will be opened.
-               #
-               retry_delay = 1
-
                #  NOTE: All configuration settings are enforced.  If a
                #  connection is closed because of 'idle_timeout',
                #  'uses', or 'lifetime', then the total number of
index 779752b..c646da0 100644 (file)
@@ -18,6 +18,13 @@ linelog {
        filename = ${logdir}/linelog
 
        #
+       #  Most file systems can handly nearly the full range of UTF-8
+       #  characters.  Ones that can deal with a limited range should
+       #  set this to "yes".
+       #
+       escape_filenames = no
+
+       #
        #  The Unix-style permissions on the log file.
        #
        #  Depending on format string, the log file may contain secret or
@@ -25,20 +32,55 @@ linelog {
        #  restrictive as possible.
        permissions = 0600
 
-       #
-       # 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 = ${security.group}
-
-       #
-       # If logging via syslog, the facility can be set here. Otherwise
-       # the syslog_facility option in radiusd.conf will be used.
-       #
-       # syslog_facility = daemon
-
+       #  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 = ${security.group}
+
+       #  Syslog facility (if logging via syslog).
+       #  Defaults to the syslog_facility config item in radiusd.conf.
+       #  Standard facilities are:
+       #  - kern        Messages generated by the kernel.  These cannot
+       #                be generated by any user processes.
+       #  - user        Messages generated by random user processes.
+       #                This is the default facility identifier if
+       #                none is specified.
+       #  - mail        The mail system.
+       #  - daemon      System daemons, such as routed(8), that are not
+       #                provided for explicitly by other facilities.
+       #  - auth        The authorization system: login(1), su(1),
+       #                getty(8), etc.
+       #  - lpr         The line printer spooling system: cups-lpd(8),
+       #                cupsd(8), etc.
+       #  - news        The network news system.
+       #  - uucp        The uucp system.
+       #  - cron        The cron daemon: cron(8).
+       #  - authpriv    The same as LOG_AUTH, but logged to a file
+       #                readable only by selected individuals.
+       #  - ftp         The file transfer protocol daemons: ftpd(8),
+       #                tftpd(8).
+       #  - local[0-7]  Reserved for local use.
+#      syslog_facility = daemon
+
+       #  Syslog severity (if logging via syslog). Defaults to info.
+       #  Possible values are:
+       #  - emergency   A panic condition.  This is normally broadcast
+       #                to all users.
+       #  - alert       A condition that should be corrected immediately,
+       #                such as a corrupted system database.
+       #  - critical    Critical conditions, e.g., hard device errors.
+       #  - error       Errors.
+       #  - warning     Warning messages.
+       #  - notice      Conditions that are not error conditions, but
+       #                should possibly be handled specially.
+       #  - info        Informational messages.
+       #  - debug       Messages that contain information normally of use
+       #                only when debugging a program.
+#      syslog_severity = info
+
+       #  If logging via syslog, the severity can be set here.
+       #  Defaults to info.
        #
        #  The default format string.
        format = "This is a log message for %{User-Name}"
@@ -61,9 +103,15 @@ linelog {
        #  needed to configure one "linelog" module for each log message.
 
        #
-       #  Reference the Packet-Type (Access-Request, etc.)  If it doesn't
-       #  exist, reference the "format" entry, above.
-       reference = "messages.%{%{Packet-Type}:-default}"
+       #  Reference the Packet-Type (Access-Accept, etc.)  If it doesn't
+       #  exist, reference the "defaukt" entry.
+       #
+       #  This is for "linelog" being used in the post-auth section
+       #  If you want to use it in "authorize", you need to change
+       #  the reference to "messages.%{%{Packet-Type}:-default}",
+       #  and then add the appropriate messages.
+       #
+       reference = "messages.%{%{reply:Packet-Type}:-default}"
 
        #
        #  The messages defined here are taken from the "reference"
@@ -72,8 +120,8 @@ linelog {
        messages {
                default = "Unknown packet type %{Packet-Type}"
 
-               Access-Request = "Requested access: %{User-Name}"
-               Access-Reject = "Rejected access: %{User-Name}"
+               Access-Accept = "Accepted user: %{User-Name}"
+               Access-Reject = "Rejected user: %{User-Name}"
                Access-Challenge = "Sent challenge: %{User-Name}"
        }
 }
@@ -95,7 +143,7 @@ linelog log_accounting {
 
        #
        #  Another example:
-       #      
+       #
        #
        Accounting-Request {
                Start = "Connect: [%{User-Name}] (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} ip %{Framed-IP-Address})"
@@ -104,10 +152,10 @@ linelog log_accounting {
                #  Don't log anything for these packets.
                Alive = ""
 
-               Accounting-On = "NAS %{Packet-Src-IP-Address} (%{NAS-IP-Address}) just came online"
-               Accounting-Off = "NAS %{Packet-Src-IP-Address} (%{NAS-IP-Address}) just went offline"
+               Accounting-On = "NAS %{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}} (%{%{NAS-IP-Address}:-%{NAS-IPv6-Address}}) just came online"
+               Accounting-Off = "NAS %{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}} (%{%{NAS-IP-Address}:-%{NAS-IPv6-Address}}) just went offline"
 
                # don't log anything for other Acct-Status-Types.
-               unknown = "NAS %{Packet-Src-IP-Address} (%{NAS-IP-Address}) sent unknown Acct-Status-Type %{Acct-Status-Type}"
+               unknown = "NAS %{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}} (%{%{NAS-IP-Address}:-%{NAS-IPv6-Address}}) sent unknown Acct-Status-Type %{Acct-Status-Type}"
        }
 }
index 2170df1..4673fa7 100644 (file)
@@ -66,6 +66,90 @@ mschap {
        #
 #      ntlm_auth_timeout = 10
 
+       # An alternative to using ntlm_auth is to connect to the
+       # winbind daemon directly for authentication. This option
+       # is likely to be faster and may be useful on busy systems,
+       # but is less well tested.
+       #
+       # Using this option requires libwbclient from Samba 4.2.1
+       # or later to be installed. Make sure that ntlm_auth above is
+       # commented out.
+       #
+#      winbind_username = "%{mschap:User-Name}"
+#      winbind_domain = "%{mschap:NT-Domain}"
+
+       #
+       #  Information for the winbind connection pool.  The configuration
+       #  items below are the same for all modules which use the new
+       #  connection pool.
+       #
+       pool {
+               #  Connections to create during module instantiation.
+               #  If the server cannot create specified number of
+               #  connections during instantiation it will exit.
+               #  Set to 0 to allow the server to start without the
+               #  winbind daemon being available.
+               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.
+               #
+               #  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
+               #
+               #  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
+               #
+               #  0 means "infinite"
+               uses = 0
+
+               #  The number of seconds to wait after the server tries
+               #  to open a connection, and fails.  During this time,
+               #  no new connections will be opened.
+               retry_delay = 30
+
+               #  The lifetime (in seconds) of the connection
+               #
+               #  NOTE: A setting of 0 means infinite (no limit).
+               lifetime = 86400
+
+               #  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
+
+               #  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.
+       }
+
        passchange {
                # This support MS-CHAPv2 (not v1) password change
                # requests.  See doc/mschap.rst for more IMPORTANT
index 2a44ef6..9a575bf 100644 (file)
@@ -8,6 +8,15 @@
 #
 #  There are no configuration entries for this module.
 #
+#  The MS-CHAP module will automatically talk to OpenDirectory, if the
+#  server is built on an OSX machine.  However, you must also set
+#  dsAttrTypeNative:apple-enabled-auth-mech attribute in the
+#  /config/dirserv OpenDirectory record.  You will probably also need
+#  to change the user passwords in order to re-generate the
+#  appropriate hashes.
+#
+#  See also https://discussions.apple.com/thread/6053980?tstart=0
+#
 opendirectory {
 
 }
index 8d9a041..0ef8675 100644 (file)
@@ -25,55 +25,69 @@ redis {
        #  connection pool.
        #
        pool {
-               # Number of connections to start
+               #  Connections to create during module instantiation.
+               #  If the server cannot create specified number of
+               #  connections during instantiation it will exit.
+               #  Set to 0 to allow the server to start without the
+               #  web service being available.
                start = ${thread[pool].start_servers}
 
-               # Minimum number of connections to keep open
+               #  Minimum number of connections to keep open
                min = ${thread[pool].min_spare_servers}
 
-               # 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.
+               #  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.
+               #  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.  This should be less than or equal to "max" above.
+               #  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
+               #  Number of uses before the connection is closed
                #
-               # NOTE: A setting of 0 means infinite (no limit).
+               #  0 means "infinite"
                uses = 0
 
-               # The lifetime (in seconds) of the connection
+               #  The number of seconds to wait after the server tries
+               #  to open a connection, and fails.  During this time,
+               #  no new connections will be opened.
+               retry_delay = 30
+
+               #  The lifetime (in seconds) of the connection
                #
-               # NOTE: A setting of 0 means infinite (no limit).
+               #  NOTE: A setting of 0 means infinite (no limit).
                lifetime = 86400
 
-               # The pool is checked for free connections every
-               # "cleanup_interval".  If there are free connections,
-               # then one of them is closed.
+               #  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.
+               #  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).
+               #  NOTE: A setting of 0 means infinite (no timeout).
                idle_timeout = 600
 
-               # 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 2897c05..19843c7 100644 (file)
@@ -8,10 +8,10 @@ rest {
 #              ca_file = ${certdir}/cacert.pem
 #              ca_path = ${certdir}
 
-#              certificate_file        = /path/to/radius.crt
+#              certificate_file        = /path/to/radius.crt
 #              private_key_file        = /path/to/radius.key
 #              private_key_password    = "supersecret"
-#              random_file             = ${certdir}/random
+#              random_file             = /dev/urandom
 
                #  Server certificate verification requirements.  Can be:
                #    "no"  (don't even bother trying)
@@ -19,7 +19,7 @@ rest {
                #          trusted CAs)
                #
                #  The default is "yes"
-#              check_cert     = "yes"
+#              check_cert = yes
 
                #  Server certificate CN verification requirements.  Can be:
                #    "no"  (don't even bother trying)
@@ -27,7 +27,7 @@ rest {
                #          in the URI)
                #
                #  The default is "yes"
-#              check_cert_cn  = "yes"
+#              check_cert_cn = yes
        }
 
        # rlm_rest will open a connection to the server specified in connect_uri
@@ -77,30 +77,92 @@ rest {
        #
        #  control:REST-HTTP-Header attributes will be consumed after each call
        #  to the rest module, and each %{rest:} expansion.
+
+       #
+       #  Body encodings are the same for requests and responses
+       #
+       #  POST - All attributes and values are urlencoded
+       #  [outer.][<list>:]<attribute0>=<value0>&[outer.][<list>:]<attributeN>=<valueN>
+       #
+       #  JSON - All attributes and values are escaped according to the JSON specification
+       #  {
+       #      "<attribute0>":{
+       #          "type":"<type0>",
+       #          "value":[<value0>,<value1>,<valueN>]
+       #      },
+       #      "<attribute1>":{
+       #          "type":"<type1>",
+       #          "value":[...]
+       #      },
+       #      "<attributeN>":{
+       #          "type":"<typeN>",
+       #          "value":[...]
+       #      },
+       #  }
+       #
+       #  The response format adds three optional fields:
+       #  - do_xlat    If true, any values will be xlat expanded. Defaults to true.
+       #  - is_json    If true, any nested JSON data will be copied to the attribute
+       #               in string form. Defaults to true.
+       #  - op         Controls how the attribute is inserted into the target list.
+       #               Defaults to ':='.
+       #  {
+       #      "<attribute0>":{
+       #          "is_json":<bool>,
+       #          "do_xlat":<bool>,
+       #          "op":"<operator>",
+       #          "value":[<value0>,<value1>,<valueN>]
+       #      },
+       #      "<attribute1>":"value",
+       #      "<attributeN>":[<value0>,<value1>,<valueN>]
+       #  }
+
+       #
+       #  Module return codes are determined by HTTP response codes. These vary depending on the
+       #  section.
+       #
+       #  If the body is processed and found to be malformed or unsupported fail will be returned.
+       #  If the body is processed and found to contain attribute updated will be returned,
+       #  except in the case of a 401 code.
+       #
+
+       #  Authorize/Authenticate
        #
+       #  Code   Meaning       Process body  Module code
+       #  404    not found     no            notfound
+       #  410    gone          no            notfound
+       #  403    forbidden     no            userlock
+       #  401    unauthorized  yes           reject
+       #  204    no content    no            ok
+       #  2xx    successful    yes           ok/updated
+       #  5xx    server error  no            fail
+       #  xxx    -             no            invalid
        authorize {
-               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?section=authorize"
-               method = "get"
+               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?action=authorize"
+               method = 'get'
                tls = ${..tls}
        }
        authenticate {
-               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?section=authenticate"
-               method = "get"
+               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?action=authenticate"
+               method = 'get'
                tls = ${..tls}
        }
+
+       #  Accounting/Post-auth
+       #
+       #  Code   Meaning       Process body  Module code
+       #  204    no content    no            ok
+       #  2xx    successful    yes           ok/updated
+       #  5xx    server error  no            fail
+       #  xxx    -             no            invalid
        accounting {
-               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?section=accounting"
-               method = "post"
-               tls = ${..tls}
-       }
-       session {
-               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?section=checksimul"
-               method = "post"
+               uri = "${..connect_uri}/user/%{User-Name}/sessions/%{Acct-Unique-Session-ID}"
+               method = 'post'
                tls = ${..tls}
        }
        post-auth {
-               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?section=post-auth"
-               method = "post"
+               uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?action=post-auth"
+               method = 'post'
                tls = ${..tls}
        }
 
@@ -109,50 +171,60 @@ rest {
        #  modules, for all kinds of connection-related activity.
        #
        pool {
-               # Number of connections to start
+               #  Connections to create during module instantiation.
+               #  If the server cannot create specified number of
+               #  connections during instantiation it will exit.
+               #  Set to 0 to allow the server to start without the
+               #  web service being available.
                start = ${thread[pool].start_servers}
 
-               # Minimum number of connections to keep open
+               #  Minimum number of connections to keep open
                min = ${thread[pool].min_spare_servers}
 
-               # 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.
+               #  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.
+               #  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.  This should be less than or equal to "max" above.
+               #  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
+               #  Number of uses before the connection is closed
                #
-               # NOTE: A setting of 0 means infinite (no limit).
+               # 0 means "infinite"
                uses = 0
 
-               # The lifetime (in seconds) of the connection
-               #
-               # NOTE: A setting of 0 means infinite (no limit).
+               #  The number of seconds to wait after the server tries
+               #  to open a connection, and fails.  During this time,
+               #  no new connections will be opened.
+               retry_delay = 30
+
+               #  The lifetime (in seconds) of the connection
                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 (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 a628617..be97d3e 100644 (file)
@@ -41,9 +41,13 @@ sql {
 #              # Path to the sqlite database
 #              filename = "/tmp/freeradius.db"
 #
+#              # How long to wait for write locks on the database to be
+#              # released (in ms) before giving up.
+#              busy_timeout = 200
+#
 #              # 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.
+#              # contained within the bootstrap file will be executed.
 #              bootstrap = "${modconfdir}/${..:name}/main/sqlite/schema.sql"
 #      }
 #
@@ -59,11 +63,16 @@ sql {
 #
 #              # If yes, (or auto and libmysqlclient reports warnings are
 #              # available), will retrieve and log additional warnings from
-#              # the server, if an error has occured. Defaults to 'auto'
+#              # the server if an error has occured. Defaults to 'auto'
 #              warnings = auto
 #      }
 #
 #      postgresql {
+#
+#              # unlike MySQL, which has a tls{} connection configuration, postgresql
+#              # uses its connection parameters - see the radius_db option below in
+#              # this file
+#
 #              # Send application_name to the postgres server
 #              # Only supported in PG 9.0 and greater. Defaults to no.
 #              send_application_name = yes
@@ -93,6 +102,10 @@ sql {
        # If you're using postgresql this can also be used instead of the connection info parameters
 #      radius_db = "dbname=radius host=localhost user=radius password=raddpass"
 
+        # Postgreql doesn't take tls{} options in its module config like mysql does - if you want to
+        # use SSL connections then use this form of connection info parameter
+#      radius_db = "host=localhost port=5432 dbname=radius user=radius password=raddpass sslmode=verify-full sslcert=/etc/ssl/client.crt sslkey=/etc/ssl/client.key sslrootcert=/etc/ssl/ca.crt" 
+
        # If you want both stop and start records logged to the
        # same SQL table, leave this as is.  If you want them in
        # different tables, put the start table in acct_table1
@@ -129,6 +142,10 @@ sql {
        # issues with authorization queries.
 #      logfile = ${logdir}/sqllog.sql
 
+       #  Set the maximum query duration and connection timeout
+       #  for rlm_sql_mysql.
+#      query_timeout = 5
+
        #  As of version 3.0, the "pool" section has replaced the
        #  following configuration items:
        #
@@ -162,59 +179,61 @@ sql {
        #       }
        #
        pool {
-               # Number of connections to start
-               start = 5
+               #  Connections to create during module instantiation.
+               #  If the server cannot create specified number of
+               #  connections during instantiation it will exit.
+               #  Set to 0 to allow the server to start without the
+               #  database being available.
+               start = ${thread[pool].start_servers}
 
-               # Minimum number of connections to keep open
-               min = 4
+               #  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.
+               #  Maximum number of connections
                #
-               # 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"
+               #  If these connections are all in use and a new one
+               #  is requested, the request will NOT get a connection.
                #
-               # Setting 'max' to MORE than the number of threads means
-               # that there are more connections than necessary.
+               #  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.
-               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 connection is closed
+               #  Number of uses before the connection is closed
                #
-               # 0 means "infinite"
+               #  0 means "infinite"
                uses = 0
 
+               #  The number of seconds to wait after the server tries
+               #  to open a connection, and fails.  During this time,
+               #  no new connections will be opened.
+               retry_delay = 30
+
                # 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
 
-               # The number of seconds to wait after the server tries
-               # to open a connection, and fails.  During this time,
-               # no new connections will be opened.
+               #  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.
                #
-               retry_delay = 1
-
-               # 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.
        }
 
        # Set to 'yes' to read radius clients from the database ('nas' table)
@@ -224,6 +243,18 @@ sql {
        # Table to keep radius client info
        client_table = "nas"
 
+       #
+       # The group attribute specific to this instance of rlm_sql
+       #
+
+       # This entry should be used for additional instances (sql foo {})
+       # of the SQL module.
+#      group_attribute = "${.:instance}-SQL-Group"
+
+       # This entry should be used for the default instance (sql {})
+       # of the SQL module.
+       group_attribute = "SQL-Group"
+
        # Read database-specific queries
        $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
 }
index 89d6d40..6361ca4 100644 (file)
 #  counter records (usually 'User-Name').
 #
 #  The 'query' parameter specifies the SQL query used to get
-#  the current Counter value from the database. There are 3
+#  the current Counter value from the database. There are 2
 #  parameters that can be used in the query:
-#              %     unix time value of beginning of reset period
-#              %     unix time value of end of reset period
+#              %%b     unix time value of beginning of reset period
+#              %%e     unix time value of end of reset period
 #
 #  The 'check_name' parameter is the name of the 'check'
 #  attribute to use to access the counter in the 'users' file
index cf18b7e..f555db7 100644 (file)
@@ -14,8 +14,8 @@ yubikey {
        #  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.
+       #  If enabled and successful, the value of &request:User-Password will be
+       #  truncated and &request:Yubikey-OTP will be added.
        #
 #      split = yes
 
@@ -25,8 +25,8 @@ yubikey {
        #  The module itself does not provide persistent storage as this
        #  would be duplicative of functionality already in the server.
        #
-       #  Yubikey authentication needs two attributes
-       #  retrieved from persistent storage:
+       #  Yubikey authentication needs two attributes retrieved from
+       #  persistent storage:
        #    * &control:Yubikey-Key        - The AES key used to decrypt the OTP data.
        #                                    The Yubikey-Public-Id and/or User-Name
        #                                    attributes may be used to retrieve the key.
@@ -41,7 +41,9 @@ yubikey {
        #  is called.
        #
        #  These attributes are available after authorization:
-       #    * &request:Yubikey-Public-ID  - The public portion of the OTP string
+       #    * &request:Yubikey-Public-ID  - The public portion of the OTP string.
+       #  and additionally if 'split' is set:
+       #    * &request:Yubikey-OTP        - The OTP portion of User-Password.
        #
        #  These attributes are available after authentication (if successful):
        #    * &request:Yubikey-Private-ID - The encrypted ID included in OTP data,
@@ -94,55 +96,63 @@ yubikey {
                #  Connection pool parameters
                #
                pool {
-                       # Number of connections to start
+                       #  Connections to create during module instantiation.
+                       #  If the server cannot create specified number of
+                       #  connections during instantiation it will exit.
+                       #  Set to 0 to allow the server to start without the
+                       #  yubikey server being available.
                        start = ${thread[pool].start_servers}
 
-                       # Minimum number of connections to keep open
+                       #  Minimum number of connections to keep open
                        min = ${thread[pool].min_spare_servers}
 
-                       # 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.
+                       #  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
+                       #  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'
                        #
-                       # 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}
+                       #  Setting 'max' to MORE than the number of threads means
+                       #  that there are more connections than necessary.
+                       max = ${thread[pool].max_servers}
 
-                       # Number of uses before the connection is closed
+                       #  Number of uses before the connection is closed
                        #
-                       # NOTE: A setting of 0 means infinite (no limit).
+                       #  NOTE: A setting of 0 means infinite (no limit).
                        uses = 0
 
-                       # The lifetime (in seconds) of the connection
+                       #  The number of seconds to wait after the server tries
+                       #  to open a connection, and fails.  During this time,
+                       #  no new connections will be opened.
+                       retry_delay = 30
+
+                       #  The lifetime (in seconds) of the connection
                        #
-                       # NOTE: A setting of 0 means infinite (no limit).
+                       #  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.
+                       #  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).
+                       #  NOTE: A setting of 0 means infinite (no timeout).
                        idle_timeout = 60
 
-                       # Cycle over all connections in a pool instead of concentrating
-                       # connection use on a few connections.
+                       #  Cycle over all connections in a pool instead of concentrating
+                       #  connection use on a few connections.
                        spread = yes
 
-                       # 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 50414d0..1ff7ac5 100644 (file)
@@ -16,4 +16,6 @@ DEFAULT
        Reply-Message =* ANY,
        MS-CHAP-Error =* ANY,
        Proxy-State =* ANY,
-       Error-Cause =* ANY
+       FreeRADIUS-Response-Delay =* ANY,
+       FreeRADIUS-Response-Delay-USec =* ANY
+
index 21a3af9..5d889ea 100644 (file)
 # These rules allow:
 #       o Only Login-User Service-Type ( no framed/ppp sessions )
 #       o Telnet sessions only ( no rlogin, tcp-clear )
-#       o Login hosts of either 192.0.2.1 or 192.0.2.2
+#       o Login host of 192.0.2.1
 #
 #tisp
 #      Service-Type == Login-User,
 #      Login-Service == Telnet,
 #      Login-TCP-Port == 23,
-#      Login-IP-Host == 192.0.2.1,
-#      Login-IP-Host == 192.0.2.2
+#      Login-IP-Host == 192.0.2.1
 
 #
 # The following example can be used for a home server which is only
 #
 
 DEFAULT
-       Service-Type == Framed-User,
-       Service-Type == Login-User,
-       Login-Service == Telnet,
-       Login-Service == Rlogin,
-       Login-Service == TCP-Clear,
-       Login-TCP-Port <= 65536,
        Framed-IP-Address == 255.255.255.254,
        Framed-IP-Netmask == 255.255.255.255,
-       Framed-Protocol == PPP,
-       Framed-Protocol == SLIP,
-       Framed-Compression == Van-Jacobson-TCP-IP,
        Framed-MTU >= 576,
        Framed-Filter-ID =* ANY,
        Reply-Message =* ANY,
index 486a9b2..cec4c02 100644 (file)
@@ -34,7 +34,7 @@ use warnings;
 use Data::Dumper;
 
 # Bring the global hashes into the package scope
-our (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK);
+our (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK, %RAD_STATE);
 
 # This is hash wich hold original request from radius
 #my %RAD_REQUEST;
@@ -42,9 +42,24 @@ our (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK);
 #my %RAD_REPLY;
 #This is for check items
 #my %RAD_CHECK;
+# This is the session-sate
+#my %RAD_STATE;
 # This is configuration items from "config" perl module configuration section
 #my %RAD_PERLCONF;
 
+# Multi-value attributes are mapped to perl arrayrefs.
+#
+#  update request {
+#    Filter-Id := 'foo'
+#    Filter-Id += 'bar'
+#  }
+#
+# This results to the following entry in %RAD_REQUEST:
+#
+#  $RAD_REQUEST{'Filter-Id'} = [ 'foo', 'bar' ];
+#
+# Likewise, you can assign an arrayref to return multi-value attributes
+
 #
 # This the remapping of return values
 #
index 97c4661..dbfb097 100644 (file)
@@ -5,10 +5,10 @@
 #  below
 #
 query = "\
-       SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
+       SELECT SUM(acctsessiontime - GREATEST((%%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
        FROM radacct \
        WHERE username = '%{${key}}' \
-       AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'"
+       AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%%b'"
 
 #
 #  This query ignores calls that started in a previous
@@ -19,15 +19,15 @@ query = "\
 #      SELECT SUM(acctsessiontime) \
 #      FROM radacct \
 #      WHERE username = '%{${key}}' \
-#      AND acctstarttime > FROM_UNIXTIME('%b')"
+#      AND acctstarttime > FROM_UNIXTIME('%%b')"
 
 #
 #  This query is the same as above, but demonstrates an
-#  additional counter parameter '%e' which is the
+#  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')"
+#      AND acctstarttime BETWEEN FROM_UNIXTIME('%%b') AND FROM_UNIXTIME('%%e')"
index 97e1bc5..73e2ca3 100644 (file)
@@ -1,5 +1,5 @@
 query = "\
-       SELECT TIMESTAMPDIFF(SECOND, acctstarttime, NOW()) \
+       SELECT IFNULL( MAX(TIME_TO_SEC(TIMEDIFF(NOW(), acctstarttime))),0) \
        FROM radacct \
        WHERE UserName='%{${key}}' \
        ORDER BY acctstarttime \
index 6d93d15..8999765 100644 (file)
@@ -5,10 +5,10 @@
 #  below
 #
 query = "\
-       SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
+       SELECT SUM(acctsessiontime - GREATEST((%%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
        FROM radacct \
        WHERE username='%{${key}}' \
-       AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'"
+       AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%%b'"
 
 #
 #  This query ignores calls that started in a previous
@@ -19,16 +19,16 @@ query = "\
 #      SELECT SUM(acctsessiontime) \
 #      FROM radacct\
 #      WHERE username='%{${key}}' \
-#      AND acctstarttime > FROM_UNIXTIME('%b')"
+#      AND acctstarttime > FROM_UNIXTIME('%%b')"
 
 #
 #  This query is the same as above, but demonstrates an
-#  additional counter parameter '%e' which is the
+#  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')"
+#      AND acctstarttime BETWEEN FROM_UNIXTIME('%%b') \
+#      AND FROM_UNIXTIME('%%e')"
index 64802bf..19bbd22 100644 (file)
@@ -5,10 +5,10 @@
 #  below
 #
 query = "\
-       SELECT SUM(AcctSessionTime - GREATER((%b - AcctStartTime::ABSTIME::INT4), 0)) \
+       SELECT SUM(AcctSessionTime - GREATER((%%b - AcctStartTime::ABSTIME::INT4), 0)) \
        FROM radacct \
        WHERE UserName='%{${key}}' \
-       AND AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%b'"
+       AND AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%%b'"
 
 #
 #  This query ignores calls that started in a previous
@@ -19,16 +19,16 @@ query = "\
 #      SELECT SUM(AcctSessionTime) \
 #      FROM radacct \
 #      WHERE UserName='%{${key}}' \
-#      AND AcctStartTime::ABSTIME::INT4 > '%b'"
+#      AND AcctStartTime::ABSTIME::INT4 > '%%b'"
 
 #
 #  This query is the same as above, but demonstrates an
-#  additional counter parameter '%e' which is the
+#  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'"
+#      AND AcctStartTime::ABSTIME::INT4 BETWEEN '%%b' \
+#      AND '%%e'"
index c4ce096..6ec4c4e 100644 (file)
@@ -1,5 +1,5 @@
 query = "\
-       SELECT TIME_TO_SEC(TIMEDIFF(NOW(), acctstarttime)) \
+       SELECT EXTRACT(EPOCH FROM (NOW() - acctstarttime)) \
        FROM radacct \
        WHERE UserName='%{${key}}' \
        ORDER BY acctstarttime \
index eb831a4..cb52180 100644 (file)
@@ -3,10 +3,10 @@
 #  involves more work for the SQL server than those
 #  below
 query = "\
-       SELECT SUM(AcctSessionTime - GREATER((%b - AcctStartTime::ABSTIME::INT4), 0)) \
+       SELECT SUM(AcctSessionTime - GREATER((%%b - AcctStartTime::ABSTIME::INT4), 0)) \
        FROM radacct \
        WHERE UserName='%{${key}}' \
-       AND AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%b'"
+       AND AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%%b'"
 
 #
 #  This query ignores calls that started in a previous
@@ -17,15 +17,15 @@ query = "\
 #      SELECT SUM(AcctSessionTime) \
 #      FROM radacct \
 #      WHERE UserName='%{${key}}' \
-#      AND AcctStartTime::ABSTIME::INT4 > '%b'"
+#      AND AcctStartTime::ABSTIME::INT4 > '%%b'"
 
 #
 #  This query is the same as above, but demonstrates an
-#  additional counter parameter '%e' which is the
+#  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'"
+#      AND AcctStartTime::ABSTIME::INT4 BETWEEN '%%b' AND '%%e'"
index 6befdcc..9a2ec38 100644 (file)
@@ -5,10 +5,10 @@
 #  below
 #
 query = "\
-       SELECT SUM(acctsessiontime - GREATEST((%b - strftime('%%s', acctstarttime)), 0)) \
+       SELECT SUM(acctsessiontime - GREATEST((%%b - strftime('%%s', acctstarttime)), 0)) \
        FROM radacct \
        WHERE username = '%{${key}}' \
-       AND (strftime('%%s', acctstarttime) + acctsessiontime) > %b"
+       AND (strftime('%%s', acctstarttime) + acctsessiontime) > %%b"
 
 #
 #  This query ignores calls that started in a previous
@@ -19,15 +19,15 @@ query = "\
 #      SELECT SUM(acctsessiontime) \
 #      FROM radacct \
 #      WHERE \username = '%{${key}}' \
-#      AND acctstarttime > %b"
+#      AND acctstarttime > %%b"
 
 #
 #  This query is the same as above, but demonstrates an
-#  additional counter parameter '%e' which is the
+#  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"
+#      AND acctstarttime BETWEEN %%b \
+#      AND %%e"
index 5bb8140..5262097 100644 (file)
@@ -5,10 +5,10 @@
 #  below
 #
 query = "\
-       SELECT SUM(acctsessiontime - GREATEST((%b - strftime('%%s', acctstarttime)), 0)) \
+       SELECT SUM(acctsessiontime - GREATEST((%%b - strftime('%%s', acctstarttime)), 0)) \
        FROM radacct \
        WHERE username = '%{${key}}' AND \
-       (strftime('%%s', acctstarttime) + acctsessiontime) > %b"
+       (strftime('%%s', acctstarttime) + acctsessiontime) > %%b"
 
 #
 #  This query ignores calls that started in a previous
@@ -19,16 +19,16 @@ query = "\
 #      SELECT SUM(acctsessiontime) \
 #      FROM radacct \
 #      WHERE username = '%{${key}}' \
-#      AND acctstarttime > %b"
+#      AND acctstarttime > %%b"
 
 #
 #  This query is the same as above, but demonstrates an
-#  additional counter parameter '%e' which is the
+#  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"
+#      AND acctstarttime BETWEEN %%b \
+#      AND %%e"
index 811e432..56ece6e 100644 (file)
@@ -123,22 +123,37 @@ accounting {
 
                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) \
+                               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-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        '%S', \
                                        '0', \
@@ -163,9 +178,7 @@ accounting {
                                        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}' \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-ID}' \
                                AND AcctStopTime = 0"
                }
 
@@ -174,19 +187,29 @@ accounting {
                                UPDATE ${....acct_table1} \
                                SET \
                                        FramedIPAddress = '%{Framed-IP-Address}' \
-                               WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                               AND UserName = '%{SQL-User-Name}' \
-                               AND NASIPAddress= '%{NAS-IP-Address}' \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-ID}' \
                                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, \
+                               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}', \
@@ -194,7 +217,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port-Id}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        '%{Acct-Session-Time}', \
                                        '%{Acct-Authentic}', \
@@ -221,20 +244,32 @@ accounting {
                                        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}' \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-ID}' \
                                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, \
+                               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}', \
@@ -242,13 +277,14 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port-Id}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        '%S', \
                                        '%{Acct-Session-Time}', \
                                        '%{Acct-Authentic}', \
                                        '', \
                                        '%{Connect-Info}', \
+                                       NULL, \
                                        convert(bigint, '%{%{Acct-Input-Gigawords}:-0}' * POWER(2.0, 32)) | '%{%{Acct-Input-Octets}:-0}', \
                                        convert(bigint, '%{%{Acct-Output-Gigawords}:-0}' * POWER(2.0, 32)) | '%{%{Acct-Output-Octets}:-0}', \
                                        '%{Called-Station-Id}', \
index 18e7198..75611ad 100644 (file)
@@ -141,21 +141,21 @@ authorize_group_check_query = "\
        SELECT id, groupname, attribute, \
        Value, op \
        FROM ${groupcheck_table} \
-       WHERE groupname = '%{Sql-Group}' \
+       WHERE groupname = '%{${group_attribute}}' \
        ORDER BY id"
 
 authorize_group_reply_query = "\
        SELECT id, groupname, attribute, \
        value, op \
        FROM ${groupreply_table} \
-       WHERE groupname = '%{Sql-Group}' \
+       WHERE groupname = '%{${group_attribute}}' \
        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
+#                      - 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
@@ -246,7 +246,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
@@ -273,9 +273,7 @@ accounting {
                                        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}'"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
                }
 
                interim-update {
@@ -293,14 +291,12 @@ accounting {
                                        acctinterval    = %{integer:Event-Timestamp} - \
                                                UNIX_TIMESTAMP(@acctupdatetime_old), \
                                        framedipaddress = '%{Framed-IP-Address}', \
-                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctsessiontime = %{%{Acct-Session-Time}:-NULL}, \
                                        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}'"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
 
                        #
                        #  The update condition matched no existing sessions. Use
@@ -315,20 +311,17 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
-                                       FROM_UNIXTIME(%{integer:Event-Timestamp} - \
-                                               %{%{Acct-Session-Time}:-0}), \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp} - %{%{Acct-Session-Time}:-0}), \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                                        NULL, \
-                                       '%{Acct-Session-Time}', \
+                                       %{%{Acct-Session-Time}:-NULL}, \
                                        '%{Acct-Authentic}', \
                                        '%{Connect-Info}', \
                                        '', \
-                                       '%{%{Acct-Input-Gigawords}:-0}' << 32 | \
-                                               '%{%{Acct-Input-Octets}:-0}', \
-                                       '%{%{Acct-Output-Gigawords}:-0}' << 32 | \
-                                               '%{%{Acct-Output-Octets}:-0}', \
+                                       '%{%{Acct-Input-Gigawords}:-0}' << 32 | '%{%{Acct-Input-Octets}:-0}', \
+                                       '%{%{Acct-Output-Gigawords}:-0}' << 32 | '%{%{Acct-Output-Octets}:-0}', \
                                        '%{Called-Station-Id}', \
                                        '%{Calling-Station-Id}', \
                                        '', \
@@ -345,16 +338,14 @@ accounting {
                                UPDATE ${....acct_table2} SET \
                                        acctstoptime    = FROM_UNIXTIME(\
                                                %{integer:Event-Timestamp}), \
-                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctsessiontime = %{%{Acct-Session-Time}:-NULL}, \
                                        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}'"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
 
                        #
                        #  The update condition matched no existing sessions. Use
@@ -369,19 +360,17 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
-                                       FROM_UNIXTIME(%{integer:Event-Timestamp} - \
-                                               %{%{Acct-Session-Time}:-0}), \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp} - %{%{Acct-Session-Time}:-0}), \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
                                        FROM_UNIXTIME(%{integer:Event-Timestamp}), \
-                                       '%{Acct-Session-Time}', \
-                                       '%{Acct-Authentic}', '', \
+                                       %{%{Acct-Session-Time}:-NULL}, \
+                                       '%{Acct-Authentic}', \
+                                       '', \
                                        '%{Connect-Info}', \
-                                       '%{%{Acct-Input-Gigawords}:-0}' << 32 | \
-                                               '%{%{Acct-Input-Octets}:-0}', \
-                                       '%{%{Acct-Output-Gigawords}:-0}' << 32 | \
-                                               '%{%{Acct-Output-Octets}:-0}', \
+                                       '%{%{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}', \
@@ -392,6 +381,7 @@ accounting {
        }
 }
 
+
 #######################################################################
 # Authentication Logging Queries
 #######################################################################
index ca22f5f..2df2d3b 100644 (file)
@@ -126,7 +126,7 @@ authorize_group_reply_query = "\
 # Simultaneous Use Checking Queries
 #######################################################################
 # simul_count_query    - query for the number of current connections
-#                      - If this is not defined, no simultaneouls use checking
+#                      - 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
@@ -202,15 +202,31 @@ accounting {
 
                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, \
+                               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(\
                                        '', \
@@ -219,7 +235,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port-Id}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
                                        NULL, \
@@ -245,9 +261,7 @@ accounting {
                                        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}' \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-ID}' \
                                AND AcctStopTime IS NULL"
                }
 
@@ -261,20 +275,32 @@ accounting {
                                                ('%{%{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}' \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-ID}' \
                                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) \
+                               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}', \
@@ -282,7 +308,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port-Id}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        NULL, \
                                        '%{Acct-Session-Time}', \
@@ -314,21 +340,35 @@ accounting {
                                        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}' \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-ID}' \
                                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) \
+                               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}', \
@@ -336,7 +376,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port-Id}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        NULL, \
                                        TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
@@ -344,6 +384,7 @@ accounting {
                                        '%{Acct-Authentic}', \
                                        '', \
                                        '%{Connect-Info}', \
+                                       NULL, \
                                        '%{Acct-Input-Octets}' + \
                                                ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
                                        '%{Acct-Output-Octets}' + \
index 1a498f1..962f4f0 100644 (file)
@@ -146,13 +146,13 @@ authorize_reply_query = "\
 authorize_group_check_query = "\
        SELECT id, GroupName, Attribute, Value, op \
        FROM ${groupcheck_table} \
-       WHERE GroupName = '%{Sql-Group}' \
+       WHERE GroupName = '%{${group_attribute}}' \
        ORDER BY id"
 
 authorize_group_reply_query = "\
        SELECT id, GroupName, Attribute, Value, op \
        FROM ${groupreply_table} \
-       WHERE GroupName = '%{Sql-Group}' \
+       WHERE GroupName = '%{${group_attribute}}' \
        ORDER BY id"
 
 #######################################################################
@@ -218,27 +218,32 @@ group_membership_query = "\
 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, \
+               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 {
@@ -249,7 +254,7 @@ accounting {
                                        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}', \
+                                       AcctTerminateCause = '%{%{Acct-Terminate-Cause}:-NAS-Reboot}' \
                                WHERE AcctStopTime IS NULL \
                                AND NASIPAddress= '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
                                AND AcctStartTime <= '%S'::timestamp"
@@ -269,7 +274,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        NULLIF('%{Realm}', ''), \
                                        '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
-                                       %{%{NAS-Port}:-NULL}, \
+                                       NULLIF('%{%{NAS-Port-ID}:-%{NAS-Port}}', ''), \
                                        '%{NAS-Port-Type}', \
                                        TO_TIMESTAMP(%{integer:Event-Timestamp}), \
                                        TO_TIMESTAMP(%{integer:Event-Timestamp}), \
@@ -293,7 +298,7 @@ accounting {
                                        AcctStartTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
                                        AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
                                        ConnectInfo_start = '%{Connect-Info}' \
-                               WHERE ${...session_identifier} \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}' \
                                AND AcctStopTime IS NULL"
 
                        # and again where we don't have "AND AcctStopTime IS NULL"
@@ -303,7 +308,7 @@ accounting {
                                        AcctStartTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
                                        AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
                                        ConnectInfo_start = '%{Connect-Info}' \
-                               WHERE ${...session_identifier}"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
                }
 
                interim-update {
@@ -318,7 +323,7 @@ accounting {
                                                '%{%{Acct-Input-Octets}:-0}'::bigint), \
                                        AcctOutputOctets = (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
                                                '%{%{Acct-Output-Octets}:-0}'::bigint) \
-                               WHERE ${...session_identifier} \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}' \
                                AND AcctStopTime IS NULL"
 
                        query = "\
@@ -330,7 +335,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        NULLIF('%{Realm}', ''), \
                                        '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
-                                       %{%{NAS-Port}:-NULL}, \
+                                       NULLIF('%{%{NAS-Port-ID}:-%{NAS-Port}}', ''), \
                                        '%{NAS-Port-Type}', \
                                        TO_TIMESTAMP(%{integer:Event-Timestamp}), \
                                        TO_TIMESTAMP(%{integer:Event-Timestamp}), \
@@ -366,7 +371,7 @@ accounting {
                                        AcctTerminateCause = '%{Acct-Terminate-Cause}', \
                                        FramedIPAddress = NULLIF('%{Framed-IP-Address}', '')::inet, \
                                        ConnectInfo_stop = '%{Connect-Info}' \
-                               WHERE ${...session_identifier} \
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}' \
                                AND AcctStopTime IS NULL"
 
                        query = "\
@@ -378,7 +383,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        NULLIF('%{Realm}', ''), \
                                        '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
-                                       %{%{NAS-Port}:-NULL}, \
+                                       NULLIF('%{%{NAS-Port-ID}:-%{NAS-Port}}', ''), \
                                        '%{NAS-Port-Type}', \
                                        TO_TIMESTAMP(%{integer:Event-Timestamp} - %{%{Acct-Session-Time}:-0}), \
                                        TO_TIMESTAMP(%{integer:Event-Timestamp}), \
@@ -413,7 +418,7 @@ accounting {
                                        AcctTerminateCause = '%{Acct-Terminate-Cause}', \
                                        FramedIPAddress = NULLIF('%{Framed-IP-Address}', '')::inet, \
                                        ConnectInfo_stop = '%{Connect-Info}' \
-                               WHERE ${...session_identifier}"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
                }
 
                #
index 26d08ca..6dfacb3 100644 (file)
@@ -24,9 +24,22 @@ GRANT SELECT ON radreply TO radius;
 GRANT SELECT ON radgroupcheck TO radius;
 GRANT SELECT ON radgroupreply TO radius;
 GRANT SELECT ON radusergroup TO radius;
+GRANT SELECT ON nas TO radius;
 
 /*
  * The server can write to the accounting and post-auth logging table.
  */
 GRANT SELECT, INSERT, UPDATE on radacct TO radius;
 GRANT SELECT, INSERT, UPDATE on radpostauth TO radius;
+
+/*
+ * Grant permissions on sequences
+ */
+GRANT USAGE, SELECT ON SEQUENCE nas_id_seq TO radius;
+GRANT USAGE, SELECT ON SEQUENCE radacct_radacctid_seq TO radius;
+GRANT USAGE, SELECT ON SEQUENCE radcheck_id_seq TO radius;
+GRANT USAGE, SELECT ON SEQUENCE radgroupcheck_id_seq TO radius;
+GRANT USAGE, SELECT ON SEQUENCE radgroupreply_id_seq TO radius;
+GRANT USAGE, SELECT ON SEQUENCE radpostauth_id_seq TO radius;
+GRANT USAGE, SELECT ON SEQUENCE radreply_id_seq TO radius;
+GRANT USAGE, SELECT ON SEQUENCE radusergroup_id_seq TO radius;
index b67767a..ae1f8be 100644 (file)
@@ -131,21 +131,21 @@ authorize_group_check_query = "\
        SELECT id, groupname, attribute, \
        Value, op \
        FROM ${groupcheck_table} \
-       WHERE groupname = '%{Sql-Group}' \
+       WHERE groupname = '%{${group_attribute}}' \
        ORDER BY id"
 
 authorize_group_reply_query = "\
        SELECT id, groupname, attribute, \
        value, op \
        FROM ${groupreply_table} \
-       WHERE groupname = '%{Sql-Group}' \
+       WHERE groupname = '%{${group_attribute}}' \
        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
+#                      - 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
@@ -166,7 +166,7 @@ simul_verify_query = "\
        SELECT radacctid, acctsessionid, username, nasipaddress, nasportid, framedipaddress, \
                callingstationid, framedprotocol \
        FROM ${acct_table1} \
-       WHERE username = '%{SQL-User-Name}' \
+       WHERE username = '%{${group_attribute}}' \
        AND acctstoptime IS NULL"
 
 #######################################################################
@@ -190,13 +190,27 @@ accounting {
 #      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, \
+               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 {
@@ -234,7 +248,7 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port}', \
+                                       '%{%{NAS-Port-ID}:-%{NAS-Port}}', \
                                        '%{NAS-Port-Type}', \
                                        %{%{integer:Event-Timestamp}:-date('now')}, \
                                        %{%{integer:Event-Timestamp}:-date('now')}, \
@@ -261,9 +275,7 @@ accounting {
                                        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}'"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
                }
 
                interim-update {
@@ -278,14 +290,12 @@ accounting {
                                        acctupdatetime  = %{%{integer:Event-Timestamp}:-date('now')}, \
                                        acctinterval    = 0, \
                                        framedipaddress = '%{Framed-IP-Address}', \
-                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctsessiontime = %{%{Acct-Session-Time}:-NULL}, \
                                        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}'"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
 
                        #
                        #  The update condition matched no existing sessions. Use
@@ -300,12 +310,12 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port}', \
+                                       '%{%{NAS-Port-ID}:-%{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-Session-Time}:-NULL}, \
                                        '%{Acct-Authentic}', \
                                        '%{Connect-Info}', \
                                        '', \
@@ -328,16 +338,14 @@ accounting {
                        query = "\
                                UPDATE ${....acct_table2} SET \
                                        acctstoptime    = %{%{integer:Event-Timestamp}:-date('now')}, \
-                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctsessiontime = %{%{Acct-Session-Time}:-NULL}, \
                                        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}'"
+                               WHERE AcctUniqueId = '%{Acct-Unique-Session-Id}'"
 
                        #
                        #  The update condition matched no existing sessions. Use
@@ -352,12 +360,12 @@ accounting {
                                        '%{SQL-User-Name}', \
                                        '%{Realm}', \
                                        '%{NAS-IP-Address}', \
-                                       '%{NAS-Port}', \
+                                       '%{%{NAS-Port-ID}:-%{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-Session-Time}:-NULL}, \
                                        '%{Acct-Authentic}', \
                                        '', \
                                        '%{Connect-Info}', \
index 8a43ff3..5c1ac4b 100644 (file)
@@ -25,7 +25,7 @@ psk_authorize {
 
 abfab_client_check {
        # check that the acceptor host name is correct
-       if ("%{client:gss_acceptor_host_name}" && "%{gss-acceptor-host-name}") {
+       if ("%{client:gss_acceptor_host_name}" && &gss-acceptor-host-name) {
                if ("%{client:gss_acceptor_host_name}" != "%{gss-acceptor-host-name}") {
                        update reply {
                                Reply-Message = "GSS-Acceptor-Host-Name incorrect"
index a632e1f..60edfcf 100644 (file)
@@ -56,7 +56,7 @@ acct_unique {
 #
 insert_acct_class {
        update reply {
-               &Class = "${policy.class_value_prefix}%{md5:%t,%I,%{Packet-Src-Port},%{Packet-Src-IP-Address},%{NAS-IP-Address},%{Calling-Station-ID},%{User-Name}}"
+               &Class = "${policy.class_value_prefix}%{md5:%t,%I,%{Packet-Src-Port},%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}},%{NAS-IP-Address},%{Calling-Station-ID},%{User-Name}}"
        }
 }
 
index 57b704c..b861b96 100644 (file)
@@ -13,7 +13,7 @@
 nai_regexp = '^([^@]*)(@([-[:alnum:]]+\.[-[:alnum:].]+))?$'
 
 split_username_nai {
-       if (&User-Name =~ /${policy.nai_regexp}/) {
+       if (&User-Name && (&User-Name =~ /${policy.nai_regexp}/)) {
                update request {
                        &Stripped-User-Name := "%{1}"
                        &Stripped-User-Domain = "%{3}"
@@ -33,7 +33,7 @@ split_username_nai {
 #  If called in post-proxy we modify the proxy-reply message
 #
 split_username_nai.post-proxy {
-       if (&proxy-reply:User-Name =~ /${policy.nai_regexp}/) {
+       if (&proxy-reply:User-Name && (&proxy-reply:User-Name =~ /${policy.nai_regexp}/)) {
                update proxy-reply {
                        &Stripped-User-Name := "%{1}"
                        &Stripped-User-Domain = "%{3}"
@@ -84,7 +84,7 @@ rewrite_called_station_id {
 #  be provided by 802.1X authenticators.
 #
 rewrite_calling_station_id {
-       if (&Calling-Station-Id =~ /^${policy.mac-addr-regexp}$/i) {
+       if (&Calling-Station-Id && (&Calling-Station-Id =~ /^${policy.mac-addr-regexp}$/i)) {
                update request {
                        &Calling-Station-Id := "%{toupper:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
                }
index 17dadf6..3cc8042 100644 (file)
@@ -17,6 +17,15 @@ debug_request {
 }
 
 #
+#  Outputs the contents of the coa list in debugging (-X) mode
+#
+debug_coa {
+       if("%{debug_attr:coa:}" == '') {
+               noop
+       }
+}
+
+#
 #  Outputs the contents of the reply list in debugging (-X) mode
 #
 debug_reply {
@@ -26,10 +35,21 @@ debug_reply {
 }
 
 #
+#  Outputs the contents of the session state list in debugging (-X) mode
+#
+debug_session_state {
+       if("%{debug_attr:session-state:}" == '') {
+               noop
+       }
+}
+
+#
 #  Outputs the contents of the main lists in debugging (-X) mode
 #
 debug_all {
        debug_control
        debug_request
+       debug_coa
        debug_reply
+       debug_session_state
 }
index fc089f9..a72beeb 100644 (file)
@@ -72,9 +72,6 @@ proxy server {
 #
 #  Configuration for the proxy realms.
 #
-#  As of 2.0. the old-style "realms" file is deprecated, and is not
-#  used by FreeRADIUS.
-#
 #  As of 2.0, the "realm" configuration has changed.  Instead of
 #  specifying "authhost" and "accthost" in a realm section, the home
 #  servers are specified separately in a "home_server" section.  For
@@ -102,6 +99,9 @@ proxy server {
 #  server pool can point to multiple home servers.  Each home server
 #  can appear in one or more pools.
 #
+#  See sites-available/tls for an example of configuring home servers,
+#  pools, and realms with TLS.
+#
 
 ######################################################################
 #
index fb7d1bd..c62f4ff 100644 (file)
@@ -229,7 +229,7 @@ cleanup_delay = 5
 #
 #  Useful range of values: 256 to infinity
 #
-max_requests = 1024
+max_requests = 16384
 
 #  hostname_lookups: Log the names of clients or just their IP addresses
 #  e.g., www.freeradius.org (on) or 206.47.27.232 (off).
index 7a057fb..16820e1 100644 (file)
@@ -169,27 +169,5 @@ post-proxy {
        #  reject the EAP request.
        #
        eap
-
-       #
-       #  If the server tries to proxy a request and fails, then the
-       #  request is processed through the modules in this section.
-       #
-       #  The main use of this section is to permit robust proxying
-       #  of accounting packets.  The server can be configured to
-       #  proxy accounting packets as part of normal processing.
-       #  Then, if the home server goes down, accounting packets can
-       #  be logged to a local "detail" file, for processing with
-       #  radrelay.  When the home server comes back up, radrelay
-       #  will read the detail file, and send the packets to the
-       #  home server.
-       #
-       #  With this configuration, the server always responds to
-       #  Accounting-Requests from the NAS, but only writes
-       #  accounting packets to disk if the home server is down.
-       #
-#      Post-Proxy-Type Fail {
-#                      detail
-#      }
-}
 }
 
diff --git a/raddb/sites-available/challenge b/raddb/sites-available/challenge
new file mode 100644 (file)
index 0000000..24c9b25
--- /dev/null
@@ -0,0 +1,76 @@
+#
+#      This file gives an example of using Challenge-Response
+#
+#      In this example, the user logs in with a password, which has
+#      to be "hello".  The server will send them a challenge
+#      consisting of a random number 0..9.  The user has to respond
+#      with that number.
+#
+#
+#      $Id$
+#
+listen {
+       type = auth
+       ipaddr = *
+       port = 2000
+       virtual_server = challenge
+}
+
+server challenge {
+authorize {
+
+       #
+       #  If ther's no State attribute, then this is the request from
+       #  the user.
+       #
+       if (!State) {
+               update control {
+                       Auth-Type := Step1
+                       Cleartext-Password := "hello"
+               }
+       }
+       else {
+               #
+               #  Do authentication for step 2.
+               #  Set the "known good" password to the number
+               #  saved in the session-state list.
+               #
+               update control {
+                       Auth-Type := Step2
+                       Cleartext-Password := &session-state:Tmp-Integer-0
+               }
+       }
+}
+
+authenticate {
+       Auth-Type Step1 {
+               #  If the password doesn't match, the user is rejected
+               #  immediately.
+               pap
+
+               #
+               #  Set the random number to save.
+               #
+               update session-state {
+                       Tmp-Integer-0 := "%{randstr:n}"
+               }
+               update reply {
+                       Reply-Message := &session-state:Tmp-Integer-0
+               }
+
+               #
+               #  Send an Access-Challenge.
+               #  See raddb/policy.d/control for the definition
+               #  of "challenge"
+               #
+               challenge
+       }
+
+       Auth-Type Step2 {
+               #
+               #  Do PAP authentication with the password.
+               #
+               pap
+       }
+}
+}
index bb44730..c10f88e 100644 (file)
@@ -37,7 +37,7 @@ server coa {
        }
 
        #  When a packet is sent, it is processed through the
-       #  recv-coa section.  This applies to *both* CoA-Request and
+       #  send-coa section.  This applies to *both* CoA-Request and
        #  Disconnect-Request packets.
        send-coa {
                #  Sample module.
index 6f97878..e16363f 100644 (file)
@@ -337,15 +337,14 @@ authorize {
        #  It also sets the EAP-Type attribute in the request
        #  attribute list to the EAP type from the packet.
        #
-       #  As of 2.0, the EAP module returns "ok" in the authorize stage
-       #  for TTLS and PEAP.  In 1.x, it never returned "ok" here, so
-       #  this change is compatible with older configurations.
+       #  The EAP module returns "ok" if it is not yet ready to
+       #  authenticate the user.  The configuration below checks for
+       #  that code, and stops processing the "authorize" section if
+       #  so.
        #
-       #  The example below uses module failover to avoid querying all
-       #  of the following modules if the EAP module returns "ok".
-       #  Therefore, your LDAP and/or SQL servers will not be queried
-       #  for the many packets that go back and forth to set up TTLS
-       #  or PEAP.  The load on those servers will therefore be reduced.
+       #  Any LDAP and/or SQL servers will not be queried for the
+       #  initial set of packets that go back and forth to set up
+       #  TTLS or PEAP.
        #
        eap {
                ok = return
@@ -355,19 +354,20 @@ authorize {
        #  Pull crypt'd passwords from /etc/passwd or /etc/shadow,
        #  using the system API's to get the password.  If you want
        #  to read /etc/passwd or /etc/shadow directly, see the
-       #  passwd module in radiusd.conf.
+       #  mods-available/passwd module.
        #
 #      unix
 
        #
-       #  Read the 'users' file
+       #  Read the 'users' file.  In v3, this is located in
+       #  raddb/mods-config/files/authorize
        files
 
        #
        #  Look in an SQL database.  The schema of the database
        #  is meant to mirror the "users" file.
        #
-       #  See "Authorization Queries" in sql.conf
+       #  See "Authorization Queries" in mods-available/sql
        -sql
 
        #
@@ -597,7 +597,7 @@ accounting {
        #
        #  Log traffic to an SQL database.
        #
-       #  See "Accounting queries" in sql.conf
+       #  See "Accounting queries" in mods-available/sql
        -sql
 
        #
@@ -645,7 +645,7 @@ session {
 #      radutmp
 
        #
-       #  See "Simultaneous Use Checking Queries" in sql.conf
+       #  See "Simultaneous Use Checking Queries" in mods-available/sql
 #      sql
 }
 
@@ -695,7 +695,7 @@ post-auth {
        #
        #  After authenticating the user, do another SQL query.
        #
-       #  See "Authentication Logging Queries" in sql.conf
+       #  See "Authentication Logging Queries" in mods-available/sql
        -sql
 
        #
@@ -781,9 +781,9 @@ post-auth {
        #
        #  Uncomment the next few lines to copy the required data into
        #  the EAP-Key-Name attribute
-#      if (reply:EAP-Session-Id) {
+#      if (&reply:EAP-Session-Id) {
 #              update reply {
-#                      EAP-Key-Name := "%{reply:EAP-Session-Id}"
+#                      EAP-Key-Name := &reply:EAP-Session-Id
 #              }
 #      }
 
@@ -895,7 +895,7 @@ post-proxy {
        #  Accounting-Requests from the NAS, but only writes
        #  accounting packets to disk if the home server is down.
        #
-#      Post-Proxy-Type Fail {
+#      Post-Proxy-Type Fail-Accounting {
 #                      detail
 #      }
 }
index fd2c8b2..595b1c2 100644 (file)
@@ -3,6 +3,16 @@
 #
 #      This is a virtual server that handles DHCP.
 #
+#  See raddb/mods-available/dhcp_sqlippool for the IP Pool configuration.
+#
+#  See raddb/policy.d/dhcp_sqlippool for the "glue" code that allows
+#  the RADIUS based "sqlippool" module to be used for DHCP.
+#
+#  See raddb/mods-config/sql/ippool/ for the schemas.
+#
+#  See raddb/sites-available/dhcp for instructions on how to configure
+#  the DHCP server.
+#
 #      $Id$
 #
 ######################################################################
@@ -232,6 +242,19 @@ dhcp DHCP-Inform {
        reject
 }
 
+#
+#  For Windows 7 boxes
+#
+#dhcp DHCP-Inform {
+#      update reply {
+#              Packet-Dst-Port = 67
+#              DHCP-Message-Type = DHCP-ACK
+#              DHCP-DHCP-Server-Identifier = "%{Packet-Dst-IP-Address}"
+#              DHCP-Site-specific-28 = 0x0a00
+#      }
+#      ok
+#}
+
 dhcp DHCP-Release {
        update reply {
               &DHCP-Message-Type = DHCP-Do-Not-Respond
@@ -240,6 +263,72 @@ dhcp DHCP-Release {
 }
 
 
+dhcp DHCP-Lease-Query {
+       #  The thing being queried for is implicit
+       #  in the packets.
+
+       # has MAC, asking for IP, etc.
+       if (&DHCP-Client-Hardware-Address) {
+               # look up MAC in database
+       }
+
+       # has IP, asking for MAC, etc.
+       elsif (&DHCP-Your-IP-Address) {
+               # look up IP in database
+       }
+
+       # has host name, asking for IP, MAC, etc.
+       elsif (&DHCP-Client-Identifier) {
+               # look up identifier in database
+       }
+       else {
+               update reply {
+                       &DHCP-Message-Type = DHCP-Lease-Unknown
+               }
+
+               ok
+
+               # stop processing
+               return
+       }
+
+       #
+       #  We presume that the database lookup returns "notfound"
+       #  if it can't find anything.
+       #
+       if (notfound) {
+               update reply {
+                       &DHCP-Message-Type = DHCP-Lease-Unknown
+               }
+               ok
+               return
+       }
+
+       #
+       #       Add more logic here.  Is the lease inactive?
+       #       If so, respond with DHCP-Lease-Unassigned.
+       #
+       #       Otherwise, respond with DHCP-Lease-Active
+       #
+
+       #
+       #       Also be sure to return ALL information about
+       #       the lease.
+       #
+
+       #
+       #       The reply types are:
+       #
+       #       DHCP-Lease-Unknown
+       #       DHCP-Lease-Active
+       #       DHCP-Lease-Unassigned
+       #
+       update reply {
+               &DHCP-Message-Type = DHCP-Lease-Unassigned
+       }
+
+}
+
 }
 
 ######################################################################
index 8762011..42b358f 100644 (file)
@@ -134,8 +134,8 @@ authorize {
        #
        #  If you are using /etc/smbpasswd, and are also doing
        #  mschap authentication, the un-comment this line, and
-       #  configure the 'etc_smbpasswd' module, above.
-#      etc_smbpasswd
+       #  enable the "smbpasswd" module.
+#      smbpasswd
 
        #
        #  The ldap module reads passwords from the LDAP database.
@@ -337,7 +337,7 @@ post-auth {
                #  Let the outer session know which module failed, and why.
                #
                update outer.session-state {
-                       Module-Failure-Message := &request:Module-Failure-Message
+                       &Module-Failure-Message := &request:Module-Failure-Message
                }
        }
 }
@@ -394,28 +394,6 @@ post-proxy {
        #  reject the EAP request.
        #
        eap
-
-       #
-       #  If the server tries to proxy a request and fails, then the
-       #  request is processed through the modules in this section.
-       #
-       #  The main use of this section is to permit robust proxying
-       #  of accounting packets.  The server can be configured to
-       #  proxy accounting packets as part of normal processing.
-       #  Then, if the home server goes down, accounting packets can
-       #  be logged to a local "detail" file, for processing with
-       #  radrelay.  When the home server comes back up, radrelay
-       #  will read the detail file, and send the packets to the
-       #  home server.
-       #
-       #  With this configuration, the server always responds to
-       #  Accounting-Requests from the NAS, but only writes
-       #  accounting packets to disk if the home server is down.
-       #
-#      Post-Proxy-Type Fail {
-#                      detail
-#      }
-
 }
 
 } # inner-tunnel server block
index 7d8cb11..0cce333 100644 (file)
@@ -61,13 +61,7 @@ home_server home2.example.com {
        response_window = 6
 }
 
-#  (2) Define a virtual server to be used when both of the
-#  home servers are down.
-home_server acct_detail.example.com {
-       virtual_server = acct_detail.example.com
-}
-
-#  Put all of the servers into a pool.
+#  (2) Put all of the servers into a pool.
 home_server_pool acct_pool.example.com {
        type = load-balance     # other types are OK, too.
 
@@ -75,10 +69,6 @@ home_server_pool acct_pool.example.com {
        home_server = home2.example.com
        # add more home_server's here.
 
-       # If all home servers are down, try a home server that
-       # is a local virtual server.
-       fallback = acct_detail.example.com
-
        # for pre/post-proxy policies
        virtual_server = home.example.com
 }
@@ -92,16 +82,7 @@ realm acct_realm.example.com {
 #  (4) Define a detail file writer.
 #   See raddb/modules/detail.example.com
 
-#  (5) Define the virtual server to write the packets to the detail file
-#  This will be called when ALL home servers are down, because of the
-#  "fallback" configuration in the home server pool.
-server acct_detail.example.com {
-       accounting {
-               detail.example.com
-       }
-}
-
-#  (6) Define a virtual server to handle pre/post-proxy re-writing
+#  (5) Define a virtual server to handle pre/post-proxy re-writing
 server home.example.com {
        pre-proxy {
                #  Insert pre-proxy rules here
@@ -123,9 +104,18 @@ server home.example.com {
                #  "detail" file, where it will be read, and sent to
                #  another home server.
                #
-               Post-Proxy-Type Fail {
+               Post-Proxy-Type Fail-Accounting {
                        detail.example.com
                }
+
+               #
+               #  This section is run when there are problems
+               #  proxying Access-Request packets
+               #
+               Post-Proxy-Type Fail-Authentication {
+                       # add policies here
+               }
+
        }
 
 
index 1ba8767..eb60fa5 100644 (file)
@@ -130,7 +130,7 @@ listen {
                #  write to files in its configuration
                #  directory.
                #
-#              random_file = ${certdir}/random
+#              random_file = /dev/urandom
 
                #
                #  The default fragment size is 1K.
@@ -429,7 +429,7 @@ home_server tls {
                #       openssl dhparam -out certs/dh 1024
                #
                dh_file = ${certdir}/dh
-               random_file = ${certdir}/random
+               random_file = /dev/urandom
 
                #
                #  The default fragment size is 1K.
index 3645ff1..d68ef01 100644 (file)
@@ -37,7 +37,7 @@
 #
 #      The per-request variables are of the form %{Attribute-Name}, and
 #      are taken from the values of the attribute in the incoming
-#      request.  See 'doc/variables.txt' for more information.
+#      request.  See 'doc/configuration/variables.rst' for more information.
 
 #
 #  Standard includes, etc.
index 2919372..d19b2d4 100755 (executable)
@@ -22,7 +22,7 @@
 # Get the wrappers for the standard lsb init functions
 . /lib/lsb/init-functions
 
-# and the RHEL specific ones
+# and the distro specific ones
 . /etc/init.d/functions
 
 prog=radiusd
index 3ef1306..dff41b1 100644 (file)
@@ -1,19 +1,18 @@
 %bcond_with rlm_yubikey
 #%bcond_with experimental_modules
 
+%{!?_with_rlm_cache_memcached: %global _without_rlm_cache_memcached --without-rlm_cache_memcached}
 %{!?_with_rlm_eap_pwd: %global _without_rlm_eap_pwd --without-rlm_eap_pwd}
 %{!?_with_rlm_eap_tnc: %global _without_rlm_eap_tnc --without-rlm_eap_tnc}
 %{!?_with_rlm_yubikey: %global _without_rlm_yubikey --without-rlm_yubikey}
 
 # experimental modules
 %bcond_with rlm_idn
-%bcond_with rlm_redis
 %bcond_with rlm_ruby
 %bcond_with rlm_sql_freetds
 %bcond_with rlm_sql_oracle
 %{?_with_rlm_idn: %global _with_experimental_modules --with-experimental-modules}
 %{?_with_rlm_opendirectory: %global _with_experimental_modules --with-experimental-modules}
-%{?_with_rlm_redis: %global _with_experimental_modules --with-experimental-modules}
 %{?_with_rlm_ruby: %global _with_experimental_modules --with-experimental-modules}
 %{?_with_rlm_securid: %global _with_experimental_modules --with-experimental-modules}
 %{?_with_rlm_sql_freetds: %global _with_experimental_modules --with-experimental-modules}
@@ -22,8 +21,6 @@
 %if %{?_with_experimental_modules:1}%{!?_with_experimental_modules:0}
 %{!?_with_rlm_idn: %global _without_rlm_idn --without-rlm_idn}
 %{!?_with_rlm_opendirectory: %global _without_rlm_opendirectory --without-rlm_opendirectory}
-%{!?_with_rlm_redis: %global _without_rlm_redis --without-rlm_redis}
-%{!?_with_rlm_redis: %global _without_rlm_rediswho --without-rlm_rediswho}
 %{!?_with_rlm_ruby: %global _without_rlm_ruby --without-rlm_ruby}
 %{!?_with_rlm_securid: %global _without_rlm_securid --without-rlm_securid}
 %{!?_with_rlm_sql_freetds: %global _without_rlm_sql_freetds --without-rlm_sql_freetds}
@@ -32,7 +29,7 @@
 
 Summary: High-performance and highly configurable free RADIUS server
 Name: freeradius
-Version: 3.0.7
+Version: 3.0.10
 Release: 2%{?dist}
 License: GPLv2+ and LGPLv2+
 Group: System Environment/Daemons
@@ -42,6 +39,7 @@ Source0: ftp://ftp.freeradius.org/pub/radius/freeradius-server-%{version}.tar.bz
 Source100: freeradius-radiusd-init
 Source102: freeradius-logrotate
 Source103: freeradius-pam-conf
+Source104: radiusd.service
 
 Obsoletes: freeradius-devel
 Obsoletes: freeradius-libs
@@ -102,6 +100,18 @@ done when adding or deleting new users.
 %debug_package
 %endif
 
+%if %{?_with_rlm_cache_memcached:1}%{?!_with_rlm_cache_memcached:0}
+%package memcached
+Summary: Memcached support for freeRADIUS
+Group: System Environment/Daemons
+Requires: %{name} = %{version}-%{release}
+Requires: libmemcached
+BuildRequires: libmemcached-devel
+
+%description memcached
+Adds support for rlm_memcached as a cache driver.
+%endif
+
 %package config
 Group: System Environment/Daemons
 Summary: FreeRADIUS config files
@@ -247,7 +257,6 @@ This plugin provides Oracle support for the FreeRADIUS server project.
 %endif
 %endif
 
-%if %{?_with_rlm_redis:1}%{!?_with_rlm_redis:0}
 %package redis
 Summary: Redis support for FreeRADIUS
 Group: System Environment/Daemons
@@ -257,7 +266,6 @@ BuildRequires: hiredis-devel
 
 %description redis
 This plugin provides Redis support for the FreeRADIUS server project.
-%endif
 
 %package rest
 Summary: REST support for FreeRADIUS
@@ -346,11 +354,10 @@ export CFLAGS="$RPM_OPT_FLAGS -fpic"
         %{?_without_rlm_securid} \
         %{?_with_rlm_sql_freetds} \
         %{?_without_rlm_sql_freetds} \
-        %{?_with_rlm_redis} \
-        %{?_without_rlm_redis} \
-        %{?_without_rlm_rediswho} \
         %{?_with_rlm_ruby} \
-        %{?_without_rlm_ruby}
+        %{?_without_rlm_ruby} \
+        %{?_with_rlm_cache_memcached} \
+        %{?_without_rlm_cache_memcached} \
 #        --with-modules="rlm_wimax" \
 
 %if "%{_lib}" == "lib64"
@@ -375,7 +382,14 @@ perl -i -pe 's/^#group =.*$/group = radiusd/' $RADDB/radiusd.conf
 mkdir -p $RPM_BUILD_ROOT/var/log/radius/radacct
 touch $RPM_BUILD_ROOT/var/log/radius/{radutmp,radius.log}
 
+# For systemd based systems, that define _unitdir, install the radiusd unit
+%if %{?_unitdir:1}%{!?_unitdir:0}
+install -D -m 755 %{SOURCE104} $RPM_BUILD_ROOT/%{_unitdir}/radiusd.service
+# For SystemV install the init script
+%else
 install -D -m 755 %{SOURCE100} $RPM_BUILD_ROOT/%{initddir}/radiusd
+%endif
+
 install -D -m 644 %{SOURCE102} $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/radiusd
 install -D -m 644 %{SOURCE103} $RPM_BUILD_ROOT/%{_sysconfdir}/pam.d/radiusd
 
@@ -480,7 +494,13 @@ fi
 %doc %{docdir}/
 %config(noreplace) %{_sysconfdir}/pam.d/radiusd
 %config(noreplace) %{_sysconfdir}/logrotate.d/radiusd
+
+%if %{?_unitdir:1}%{!?_unitdir:0}
+%{_unitdir}/radiusd.service
+%else
 %{initddir}/radiusd
+%endif
+
 %dir %attr(755,radiusd,radiusd) /var/lib/radiusd
 %dir %attr(755,radiusd,radiusd) /var/run/radiusd/
 # binaries
@@ -574,6 +594,7 @@ fi
 %{_libdir}/freeradius/rlm_soh.so
 %{_libdir}/freeradius/rlm_sometimes.so
 %{_libdir}/freeradius/rlm_sql.so
+%{_libdir}/freeradius/rlm_sqlhpwippool.so
 %{_libdir}/freeradius/rlm_sql_null.so
 %{_libdir}/freeradius/rlm_sql_sqlite.so
 %{_libdir}/freeradius/rlm_sqlcounter.so
@@ -586,7 +607,6 @@ fi
 %if %{?_with_experimental_modules:1}%{!?_with_experimental_modules:0}
 %{_libdir}/freeradius/rlm_example.so
 %{_libdir}/freeradius/rlm_smsotp.so
-%{_libdir}/freeradius/rlm_sqlhpwippool.so
 %endif
 
 %files config
@@ -708,6 +728,12 @@ fi
 %doc %{_mandir}/man8/radsqlrelay.8.gz
 %doc %{_mandir}/man8/rlm_ippool_tool.8.gz
 
+%if %{?_with_rlm_cache_memcached:1}%{!?_with_rlm_cache_memcached:0}
+%files memcached
+%defattr(-,root,root)
+%{_libdir}/freeradius/rlm_cache_memcached.so
+%endif
+
 %files krb5
 %defattr(-,root,root)
 %{_libdir}/freeradius/rlm_krb5.so
@@ -740,12 +766,10 @@ fi
 %defattr(-,root,root)
 %{_libdir}/freeradius/rlm_sql_unixodbc.so
 
-%if %{?_with_rlm_redis:1}%{!?_with_rlm_redis:0}
 %files redis
 %defattr(-,root,root)
 %{_libdir}/freeradius/rlm_redis.so
 %{_libdir}/freeradius/rlm_rediswho.so
-%endif
 
 %files rest
 %defattr(-,root,root)
diff --git a/redhat/radiusd.service b/redhat/radiusd.service
new file mode 100644 (file)
index 0000000..6d70b46
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=FreeRADIUS multi-protocol policy server
+After=syslog.target network.target
+Documentation=man:radiusd(8) man:radiusd.conf(5) http://wiki.freeradius.org/ http://networkradius.com/doc/
+
+[Service]
+Type=forking
+PIDFile=/var/run/radiusd/radiusd.pid
+EnvironmentFile=-/etc/sysconfig/radiusd
+ExecStartPre=/usr/sbin/radiusd $FREERADIUS_OPTIONS -Cxm -lstdout
+ExecStart=/usr/sbin/radiusd $FREERADIUS_OPTIONS -m
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
index ea303bc..a851309 100755 (executable)
 #  of the exec instance call as follows:
 #  (See doc/configurable_failover for details)
 #  < 0 : fail      the module failed
-#  = 0 : okthe module succeeded
+#  = 0 : ok        the module succeeded
 #  = 1 : reject    the module rejected the user
 #  = 2 : fail      the module failed
-#  = 3 : okthe module succeeded
+#  = 3 : ok        the module succeeded
 #  = 4 : handled   the module has done everything to handle the request
 #  = 5 : invalid   the user's configuration entry was invalid
 #  = 6 : userlock  the user was locked out
index ed5734a..3f44994 100755 (executable)
@@ -9,12 +9,16 @@
 #
 # Copyright 2012  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
 
+PATH=/bin:/usr/bin:/usr/sbin:/sbin
 # Tag to update when we successfully manage to start the server with a new configuration
 : ${STABLE_TAG='stable'}
 
 # Descriptive name of daemon
 : ${DAEMON_DESC='FreeRADIUS'}
 
+# Init script for radiusd
+: ${DAEMON_STATUS='/etc/init.d/radiusd status'}
+
 # Command used to restart the RADIUS daemon
 : ${DAEMON_REST='radmin -e hup'}
 
@@ -65,6 +69,13 @@ conf_check () {
        return $ret
 }
 
+daemon_status () {
+       echo -n "Checking if radiusd is running ... "
+       $DAEMON_STATUS; ret=$?
+
+       return $ret
+}
+
 daemon_restart () {
        echo -n "Restarting server... "
 
@@ -113,7 +124,7 @@ if ! conf_check; then
        conf_rollback
        exit 64
 else
-       if ! daemon_restart; then
+       if daemon_status && ! daemon_restart ; then
                if ! conf_rollback; then
                        echo "WARNING: Manually verify $DAEMON_DESC status immediately!"
                        exit 64
index eb40c9d..9c0bfe6 100644 (file)
 #   replacing the line after the "mkdir"
 #
 
+#
+#  You can watch what it's doing by:
+#
+#      $ VERBOSE=1 make ... args ...
+#
+ifeq "${VERBOSE}" ""
+    Q=@
+else
+    Q=
+endif
+
 # ADD_INSTALL_RULE.exe - Parameterized "function" that adds a new rule
 #   and phony target for installing an executable.
 #
@@ -37,9 +48,9 @@ define ADD_INSTALL_RULE.exe
     # Install executable ${1}
     $${${1}_INSTALLDIR}/$(notdir ${1}): $${${1}_BUILD}/${1}
        @$(ECHO) INSTALL ${1}
-       @$${PROGRAM_INSTALL} -d -m 755 $${${1}_INSTALLDIR}
-       @$${PROGRAM_INSTALL} -c -m 755 $${BUILD_DIR}/bin/${1} $${${1}_INSTALLDIR}/
-       @$${${1}_POSTINSTALL}
+       $(Q)$${PROGRAM_INSTALL} -d -m 755 $${${1}_INSTALLDIR}
+       $(Q)$${PROGRAM_INSTALL} -c -m 755 $${BUILD_DIR}/bin/${1} $${${1}_INSTALLDIR}/
+       $(Q)$${${1}_POSTINSTALL}
 
 endef
 
@@ -57,9 +68,9 @@ define ADD_INSTALL_RULE.a
     # Install static library ${1}
     $${${1}_INSTALLDIR}/$(notdir ${1}): ${1}
        @$(ECHO) INSTALL ${1}
-       @$${PROGRAM_INSTALL} -d -m 755 $${${1}_INSTALLDIR}
-       @$${PROGRAM_INSTALL} -c -m 755 $${BUILD_DIR}/lib/${1} $${${1}_INSTALLDIR}/
-       @$${${1}_POSTINSTALL}
+       $(Q)$${PROGRAM_INSTALL} -d -m 755 $${${1}_INSTALLDIR}
+       $(Q)$${PROGRAM_INSTALL} -c -m 755 $${BUILD_DIR}/lib/${1} $${${1}_INSTALLDIR}/
+       $(Q)$${${1}_POSTINSTALL}
 
 endef
 
@@ -80,9 +91,9 @@ define ADD_INSTALL_RULE.la
     # Install libtool library ${1}
     $${${1}_INSTALLDIR}/$(notdir ${1}): $${${1}_BUILD}/${1}
        @$(ECHO) INSTALL ${1}
-       @$${PROGRAM_INSTALL} -d -m 755 $${${1}_INSTALLDIR}
-       @$${PROGRAM_INSTALL} -c -m 755 $${RELINK_FLAGS_MIN} $${BUILD_DIR}/lib/${1} $${${1}_INSTALLDIR}/
-       @$${${1}_POSTINSTALL}
+       $(Q)$${PROGRAM_INSTALL} -d -m 755 $${${1}_INSTALLDIR}
+       $(Q)$${PROGRAM_INSTALL} -c -m 755 $${RELINK_FLAGS_MIN} $${BUILD_DIR}/lib/${1} $${${1}_INSTALLDIR}/
+       $(Q)$${${1}_POSTINSTALL}
 
 endef
 
@@ -101,8 +112,8 @@ define ADD_INSTALL_RULE.man
     # Install manual page ${1}
     ${2}/$(notdir ${1}): ${1}
        @$(ECHO) INSTALL $(notdir ${1})
-       @[ -d ${2} ] || $${PROGRAM_INSTALL} -d -m 755 ${2}
-       @$${PROGRAM_INSTALL} -c -m 644 ${1} ${2}/
+       $(Q)[ -d ${2} ] || $${PROGRAM_INSTALL} -d -m 755 ${2}
+       $(Q)$${PROGRAM_INSTALL} -c -m 644 ${1} ${2}/
 
 endef
 
@@ -118,7 +129,7 @@ define ADD_INSTALL_TARGET
         ifeq "$${TGT_INSTALLDIR}" ".."
             TGT_INSTALLDIR := $${bindir}
         endif
-    else 
+    else
         ifeq "$${TGT_INSTALLDIR}" ".."
             TGT_INSTALLDIR := $${libdir}
         endif
@@ -203,7 +214,7 @@ endif
 # We also want to uninstall only when there are "install_foo" targets.
 .PHONY: uninstall
 uninstall:
-       @rm -f ${ALL_INSTALL} ./.no_such_file
+       $(Q)rm -f ${ALL_INSTALL} ./.no_such_file
 
 # Wrapper around INSTALL
 ifeq "${PROGRAM_INSTALL}" ""
@@ -225,5 +236,5 @@ install: install_ERROR
 .PHONY: install_ERROR
 install_ERROR:
        @$(ECHO) Please define INSTALL in order to enable the installation rules.
-       @exit 1
+       $(Q)exit 1
 endif
index 6d36a25..a95dc30 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+#
+#  You can watch what it's doing by:
+#
+#      $ VERBOSE=1 make ... args ...
+#
+ifeq "${VERBOSE}" ""
+    Q=@
+else
+    Q=
+endif
+
 # Add these rules only when LIBTOOL is being used.
 ifneq "${LIBTOOL}" ""
 
@@ -83,12 +94,12 @@ define ADD_TARGET_RULE.la
 
     # Create libtool library ${1}
     $${${1}_BUILD}/${1}: $${${1}_OBJS} $${${1}_PRLIBS}
-           @$(strip mkdir -p $(dir $${${1}_BUILD}/${1}))
+           $(Q)$(strip mkdir -p $(dir $${${1}_BUILD}/${1}))
            @$(ECHO) LINK $${${1}_BUILD}/${1}
-           @$${${1}_LINKER} -o $${${1}_BUILD}/${1} $${RPATH_FLAGS} $${LDFLAGS} \
+           $(Q)$${${1}_LINKER} -o $${${1}_BUILD}/${1} $${RPATH_FLAGS} $${LDFLAGS} \
                 $${${1}_LDFLAGS} $${${1}_OBJS} $${LDLIBS} $${${1}_LDLIBS} \
                $${${1}_PRLIBS}
-           @$${${1}_POSTMAKE}
+           $(Q)$${${1}_POSTMAKE}
 
     ifneq "${ANALYZE.c}" ""
         scan.${1}: $${${1}_PLISTS}
@@ -105,11 +116,11 @@ define ADD_RELINK_RULE.exe
 
     # used to fix up RPATH for ${1} on install.
     $${${1}_BUILD}/$${${1}_RELINK}: $${${1}_OBJS} $${${1}_PRBIN} $${${1}_R_PRLIBS}
-           @$(strip mkdir -p $${${1}_BUILD}/${RELINK}/)
-           @$${${1}_LINKER} -o $${${1}_BUILD}/$${RELINK}${1} $${RELINK_FLAGS} $${LDFLAGS} \
+           $(Q)$(strip mkdir -p $${${1}_BUILD}/${RELINK}/)
+           $(Q)$${${1}_LINKER} -o $${${1}_BUILD}/$${RELINK}${1} $${RELINK_FLAGS} $${LDFLAGS} \
                 $${${1}_LDFLAGS} $${${1}_OBJS} $${${1}_R_PRLIBS} \
                 $${LDLIBS} $${${1}_LDLIBS}
-           @$${${1}_POSTMAKE}
+           $(Q)$${${1}_POSTMAKE}
 endef
 
 # ADD_RELINK_RULE.la - Parametric "function" that adds a rule to relink
@@ -122,10 +133,10 @@ define ADD_RELINK_RULE.la
 
     # used to fix up RPATH for ${1} on install.
     $${${1}_BUILD}/$${${1}_RELINK}: $${${1}_OBJS} $${${1}_PRLIBS}
-           @$(strip mkdir -p $${${1}_BUILD}/${RELINK}/)
-           @$${${1}_LINKER} -o $${${1}_BUILD}/$${RELINK}${1} $${RELINK_FLAGS} $${LDFLAGS} \
+           $(Q)$(strip mkdir -p $${${1}_BUILD}/${RELINK}/)
+           $(Q)$${${1}_LINKER} -o $${${1}_BUILD}/$${RELINK}${1} $${RELINK_FLAGS} $${LDFLAGS} \
                 $${${1}_LDFLAGS} $${${1}_OBJS} $${LDLIBS} $${${1}_LDLIBS}
-           @$${${1}_POSTMAKE}
+           $(Q)$${${1}_POSTMAKE}
 
 endef
 
index 1694aee..2a9819b 100755 (executable)
@@ -52,6 +52,7 @@ function show_help
     echo "  -H <host>[:port]          Send test packets to specified host and port (defaults to 127.0.0.1)"
     echo "  -v                        Verbose mode."
     echo "  -p <number>               Run tests in parallel (defaults to 20)."
+    echo "  -s <secret>               Shared secret."
     if [ ! -z "$role_types" ]; then
         echo "  -c <cluster>              Specify cluster type one of ($cluster_types)."
         echo "  -r <type>                 Specify server role one of ($role_types)."
@@ -92,7 +93,7 @@ cluster_types=$(echo $cluster_dirs | sed 's/\s/ /g')
 
 role_types=
 for i in $cluster_dirs; do
-    for j in $(find "$TESTDIR/$i/" -mindepth 1 -maxdepth 1 -type d); do
+    for j in $(find "$TESTDIR/$(basename $i)/" -mindepth 1 -maxdepth 1 -type d); do
         role=$(basename "$j")
         if [ "$role_types" == '' ]; then
             role_types="$role"
@@ -110,7 +111,7 @@ else
     role_types=$(echo $role_types | sed 's/\s/ /g')     # Change \n back to spaces
 fi
 
-while getopts "h?H:vc:r:p:" opt; do
+while getopts "h?H:vc:r:s:p:" opt; do
     case "$opt" in
     h|\?)
         show_help
@@ -151,6 +152,10 @@ while getopts "h?H:vc:r:p:" opt; do
         role="$OPTARG"
         ;;
 
+    s)
+        secret="$OPTARG"
+        ;;
+
     p)
         if ! echo "$OPTARG" | grep -E '^[0-9]+$' > /dev/null; then
             ERROR "Non integer argument '$OPTARG' specified for -p"
index 9ed4751..8ca841e 100644 (file)
@@ -113,6 +113,7 @@ $INCLUDE dictionary.rfc6930
 $INCLUDE dictionary.rfc7055
 $INCLUDE dictionary.rfc7155
 $INCLUDE dictionary.rfc7268
+$INCLUDE dictionary.rfc7499
 
 #
 #      Mostly values which have been allocated by IANA under
@@ -136,13 +137,16 @@ $INCLUDE dictionary.alcatel-lucent.aaa
 $INCLUDE dictionary.alteon
 $INCLUDE dictionary.alvarion
 $INCLUDE dictionary.apc
+$INCLUDE dictionary.aptilo
 $INCLUDE dictionary.arbor
+$INCLUDE dictionary.arista
 $INCLUDE dictionary.aruba
 $INCLUDE dictionary.azaire
 $INCLUDE dictionary.ascend
 $INCLUDE dictionary.bay
 $INCLUDE dictionary.bintec
 $INCLUDE dictionary.bluecoat
+$INCLUDE dictionary.boingo
 $INCLUDE dictionary.broadsoft
 $INCLUDE dictionary.brocade
 $INCLUDE dictionary.bskyb
@@ -268,6 +272,7 @@ $INCLUDE dictionary.versanet
 $INCLUDE dictionary.waverider
 $INCLUDE dictionary.walabi
 $INCLUDE dictionary.wichorus
+$INCLUDE dictionary.wifialliance
 $INCLUDE dictionary.wimax
 $INCLUDE dictionary.wispr
 $INCLUDE dictionary.xedia
index cfd3fea..1b89cb2 100644 (file)
@@ -109,8 +109,16 @@ ATTRIBUTE  3GPP2-Reverse-DHHC-RC                   87      integer
 ATTRIBUTE      3GPP2-Session-Termination-Capability    88      integer
 ATTRIBUTE      3GPP2-Allowed-Persistent-TFTs           89      integer
 
-# The next set of attributes contain sub-types
-ATTRIBUTE      3GPP2-Prepaid-Acct-Quota                90      octets
+ATTRIBUTE      3GPP2-Prepaid-Acct-Quota                90      tlv
+
+ATTRIBUTE      3GPP2-Prepaid-Acct-Quota-QuotaIDentifier 90.1   integer
+ATTRIBUTE      3GPP2-Prepaid-Acct-Quota-VolumeQuota    90.2    integer
+ATTRIBUTE      3GPP2-Prepaid-Acct-Quota-VolumeQuotaOverflow 90.3       integer
+ATTRIBUTE      3GPP2-Prepaid-Acct-Quota-VolumeThreshold 90.4   integer
+ATTRIBUTE      3GPP2-Prepaid-Acct-Quota-VolumeThresholdOverflow 90.5   integer
+ATTRIBUTE      3GPP2-Prepaid-Acct-Quota-UpdateReason   90.8    short
+
+# The next two attributes contain sub-types
 ATTRIBUTE      3GPP2-Prepaid-acct-Capability           91      octets
 ATTRIBUTE      3GPP2-MIP-Lifetime                      92      octets
 
index c75b312..b6df8bf 100644 (file)
@@ -320,6 +320,13 @@ ATTRIBUTE  Alc-AA-Group-Partition-Isa-Id           156     string
 # Application-Assurance Radius Peer Information
 ATTRIBUTE      Alc-AA-Peer-Identifier                  157     string
 
+# A local configured filter policy can be extended with shared dynamic filter entries
+ATTRIBUTE      Alc-Nas-Filter-Rule-Shared              158     string
+
+# They represent a per host customization of a generic filter policy: only
+# traffic to/from the subscriber host will match against these entries
+ATTRIBUTE      Alc-Ascend-Data-Filter-Host-Spec                159      string
+
 # Relative Session-Timeout
 ATTRIBUTE      Alc-Relative-Session-Timeout            160     integer
 
@@ -340,4 +347,25 @@ VALUE      Alc-Acct-Triggered-Reason       Delegated-IPv6-Prefix-down 9
 VALUE  Alc-Acct-Triggered-Reason       Framed-IPv6-Prefix-up   10
 VALUE  Alc-Acct-Triggered-Reason       Framed-IPv6-Prefix-down 11
 
+# Used when authenticating migrant hosts
+ATTRIBUTE   Alc-Wlan-Portal-Redirect                  172 string
+
+# If a migrant host is redirected, specifies the URL
+ATTRIBUTE   Alc-Wlan-Portal-Url                       173 string
+
+# Defines the lease-time in seconds for RADIUS proxy and create-host-CoA
+# scenarios only.
+ATTRIBUTE   Alc-Lease-Time                            174 integer
+
+# The URL to which traffic matching the host IPv4 filter entry with http-redirect
+# action is redirected to
+ATTRIBUTE   Alc-Portal-Url                            177 string
+
+# names longer than the allowed maximum are treated as host setup failures
+ATTRIBUTE   Alc-SLAAC-IPv6-Pool                       181 string
+
+# The VLAN is transparently taken from the UE’s Ethernet layer and can be reflected
+# in both authentication and accounting
+ATTRIBUTE   Alc-Wlan-SSID-VLAN                        206 string
+
 END-VENDOR     Alcatel-Lucent-Service-Router
diff --git a/share/dictionary.aptilo b/share/dictionary.aptilo
new file mode 100644 (file)
index 0000000..6425dc6
--- /dev/null
@@ -0,0 +1,139 @@
+# -*- text -*-
+# Copyright (C) 2015 The FreeRADIUS Server project and contributors
+#
+##############################################################################
+# Aptilo Access Controllers - http://www.aptilo.com/aptilo-access-controller
+#
+# Version:      $Id$
+#
+#               Jorge Pereira <jpereiran@gmail.com>
+#
+##############################################################################
+
+VENDOR         Aptilo                          13209
+
+BEGIN-VENDOR   Aptilo
+
+ATTRIBUTE      Aptilo-Subnet-Name                      1       string
+ATTRIBUTE      Aptilo-Octets-Limit                     2       integer
+ATTRIBUTE      Aptilo-Gigawords-Limit                  3       integer
+ATTRIBUTE      Aptilo-Input-Octets-Limit               4       integer
+ATTRIBUTE      Aptilo-Input-Gigawords-Limit            5       integer
+ATTRIBUTE      Aptilo-Output-Octets-Limit              6       integer
+ATTRIBUTE      Aptilo-Output-Gigawords-Limit           7       integer
+ATTRIBUTE      Aptilo-Limit-Mode                       8       integer
+ATTRIBUTE      Aptilo-Apc-ID                           9       string
+ATTRIBUTE      Aptilo-Opaque-Key                       10      string
+ATTRIBUTE      Aptilo-Denied-Cause                     11      integer
+ATTRIBUTE      Aptilo-Realm-ID                         12      integer
+ATTRIBUTE      Aptilo-Ap-ID                            13      integer
+ATTRIBUTE      Aptilo-User-ID                          14      integer
+ATTRIBUTE      Aptilo-Zone                             15      string
+ATTRIBUTE      Aptilo-First-Name                       16      string
+ATTRIBUTE      Aptilo-Last-Name                        17      string
+ATTRIBUTE      Aptilo-Phone                            18      string
+ATTRIBUTE      Aptilo-Email                            19      string
+ATTRIBUTE      Aptilo-Organization                     20      string
+ATTRIBUTE      Aptilo-Access-Profile                   21      string
+ATTRIBUTE      Aptilo-Realm-Concurrent-Login           22      integer
+ATTRIBUTE      Aptilo-Auth-Result                      23      integer
+ATTRIBUTE      Aptilo-Hotline-Indicator                24      string
+ATTRIBUTE      Aptilo-User-Type                        25      integer
+ATTRIBUTE      Aptilo-Exclusive-Count                  26      integer
+ATTRIBUTE      Aptilo-Duration-Quota                   27      integer
+ATTRIBUTE      Aptilo-Volume-Quota                     28      string
+ATTRIBUTE      Aptilo-RX-Volume-Quota                  29      string
+ATTRIBUTE      Aptilo-TX-Volume-Quota                  30      string
+ATTRIBUTE      Aptilo-Resource-Quota                   31      integer
+ATTRIBUTE      Aptilo-Quota-ID                         32      string
+ATTRIBUTE      Aptilo-RX-Limit                         33      integer
+ATTRIBUTE      Aptilo-TX-Limit                         34      integer
+ATTRIBUTE      Aptilo-TRX-Limit                        35      integer
+ATTRIBUTE      Aptilo-Bw-Min-Up                        36      integer
+ATTRIBUTE      Aptilo-Bw-Max-Up                        37      integer
+ATTRIBUTE      Aptilo-Bw-Min-Down                      38      integer
+ATTRIBUTE      Aptilo-Bw-Max-Down                      39      integer
+ATTRIBUTE      Aptilo-Service-Profile                  40      string
+ATTRIBUTE      Aptilo-Automatic-Service                41      string
+ATTRIBUTE      Aptilo-Auth-Type                        42      integer
+ATTRIBUTE      Aptilo-NAS-Capabilities                 43      integer
+ATTRIBUTE      Aptilo-Service                          44      string
+ATTRIBUTE      Aptilo-Service-Profile-ID               45      integer
+ATTRIBUTE      Aptilo-Auth-Param                       50      integer
+ATTRIBUTE      Aptilo-Access-Profile-ID                53      integer
+ATTRIBUTE      Aptilo-NAS-Model                        56      string
+ATTRIBUTE      Aptilo-Debug-Option                     57      integer
+ATTRIBUTE      Aptilo-Session-Id                       58      string
+ATTRIBUTE      Aptilo-Prepaid-Capabilities             59      octets
+ATTRIBUTE      Aptilo-Octets-Quota                     60      octets
+ATTRIBUTE      Aptilo-Octets-Threshold                 61      octets
+ATTRIBUTE      Aptilo-Resource-Threshold               62      integer
+ATTRIBUTE      Aptilo-Duration-Threshold               63      integer
+ATTRIBUTE      Aptilo-Octets-Balance                   64      octets
+ATTRIBUTE      Aptilo-Resource-Balance                 65      integer
+ATTRIBUTE      Aptilo-Duration-Balance                 66      integer
+ATTRIBUTE      Aptilo-Octets-Used                      67      octets
+ATTRIBUTE      Aptilo-Resource-Used                    68      integer
+ATTRIBUTE      Aptilo-Duration-Used                    69      integer
+ATTRIBUTE      Aptilo-Octets-Request                   70      octets
+ATTRIBUTE      Aptilo-Resource-Request                 71      integer
+ATTRIBUTE      Aptilo-Duration-Request                 72      integer
+ATTRIBUTE      Aptilo-QoS-Indicator                    73      string
+
+ATTRIBUTE      Aptilo-Key-String-1                     241     string
+ATTRIBUTE      Aptilo-Key-String-2                     242     string
+ATTRIBUTE      Aptilo-Key-String-3                     243     string
+ATTRIBUTE      Aptilo-Key-String-4                     244     string
+ATTRIBUTE      Aptilo-Key-String-5                     245     string
+ATTRIBUTE      Aptilo-Key-IP-1                         246     ipaddr
+ATTRIBUTE      Aptilo-Key-IP-2                         247     ipaddr
+ATTRIBUTE      Aptilo-Key-IP-3                         248     ipaddr
+ATTRIBUTE      Aptilo-Key-IP-4                         249     ipaddr
+ATTRIBUTE      Aptilo-Key-IP-5                         250     ipaddr
+ATTRIBUTE      Aptilo-Key-Integer-1                    251     integer
+ATTRIBUTE      Aptilo-Key-Integer-2                    252     integer
+ATTRIBUTE      Aptilo-Key-Integer-3                    253     integer
+ATTRIBUTE      Aptilo-Key-Integer-4                    254     integer
+ATTRIBUTE      Aptilo-Key-Integer-5                    255     integer
+
+VALUE  Aptilo-Limit-Mode               Relative                0
+VALUE  Aptilo-Limit-Mode               Absolute                1
+
+VALUE  Aptilo-Denied-Cause             User-Not-Found          1
+VALUE  Aptilo-Denied-Cause             Wrong-Password          2
+VALUE  Aptilo-Denied-Cause             No-Zone-Access          3
+VALUE  Aptilo-Denied-Cause             Inactive-Access-Node    4
+VALUE  Aptilo-Denied-Cause             Inconsistent-Access-Node 5
+VALUE  Aptilo-Denied-Cause             Disabled-Account        6
+VALUE  Aptilo-Denied-Cause             No-Accessprofile        7
+VALUE  Aptilo-Denied-Cause             Internal-Error          8
+VALUE  Aptilo-Denied-Cause             Realm-Error             9
+VALUE  Aptilo-Denied-Cause             No-Credits              10
+VALUE  Aptilo-Denied-Cause             Max-Session             11
+VALUE  Aptilo-Denied-Cause             Remote-Server-Reject    12
+VALUE  Aptilo-Denied-Cause             Realm-Inactive          14
+VALUE  Aptilo-Denied-Cause             Opaque-Failed           15
+VALUE  Aptilo-Denied-Cause             Service-Closed          16
+VALUE  Aptilo-Denied-Cause             LDAP-Failed             17
+VALUE  Aptilo-Denied-Cause             Inactive-Account        18
+VALUE  Aptilo-Denied-Cause             Expired-Account         19
+VALUE  Aptilo-Denied-Cause             Incomplete-Account      20
+VALUE  Aptilo-Denied-Cause             License-Limit-Reached   21
+VALUE  Aptilo-Denied-Cause             Unsupported-Service     22
+VALUE  Aptilo-Denied-Cause             Ruleset-Reject          23
+VALUE  Aptilo-Denied-Cause             Ruleset-Failed          24
+
+#
+#   Is sent by the AC to indicate what type of authentication
+#   User - User initiated login, i.e. posting to /cgi-bin/login
+#   Auto - Automatic login, e.g. "new station" based login or logins made
+#          from /cgi-bin/auto
+#   IP-Request - Request for a fixed IP address if available, as a result
+#          of a client DHCP request.
+#
+VALUE  Aptilo-Auth-Type                User                    0
+VALUE  Aptilo-Auth-Type                Auto                    1
+VALUE  Aptilo-Auth-Type                IP-Request              2
+VALUE  Aptilo-Debug-Option             Simulate-EAP-TLS        1
+
+END-VENDOR     Aptilo
index 7841bee..2013fbd 100644 (file)
@@ -12,7 +12,7 @@ VENDOR                Arbor                           9694
 
 BEGIN-VENDOR   Arbor
 
-# Arbor-Privilege-Level = "sytem_admin or system_analyst or system_user"
+# Arbor-Privilege-Level = "system_admin or system_analyst or system_user"
 
 ATTRIBUTE      Arbor-Privilege-Level                   1       string
 
diff --git a/share/dictionary.arista b/share/dictionary.arista
new file mode 100644 (file)
index 0000000..27d693c
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- text -*-
+# Copyright (C) 2015 The FreeRADIUS Server project and contributors
+##############################################################################
+#
+#      Arista VSAs
+#
+##############################################################################
+
+VENDOR         Arista                          30065
+BEGIN-VENDOR   Arista
+
+ATTRIBUTE      Arista-AVPair                   1       string
+
+END-VENDOR   Arista
index 80e14fb..8196dac 100644 (file)
@@ -44,4 +44,17 @@ ATTRIBUTE    Aruba-Mdps-Device-Profile               33      string
 
 ATTRIBUTE      Aruba-AP-IP-Address                     34      ipaddr
 
+ATTRIBUTE      Aruba-AirGroup-Shared-Group             35      string
+ATTRIBUTE      Aruba-User-Group                        36      string
+ATTRIBUTE      Aruba-Network-SSO-Token                 37      string
+ATTRIBUTE      Aruba-AirGroup-Version                  38      integer
+
+ATTRIBUTE      Aruba-Port-Bounce-Host                  40      integer
+
+VALUE  Aruba-AirGroup-Device-Type      Personal-Device         1
+VALUE  Aruba-AirGroup-Device-Type      Shared-Device           2
+
+VALUE  Aruba-AirGroup-Version          AirGroup-v1             1
+VALUE  Aruba-AirGroup-Version          AirGroup-v2             2
+
 END-VENDOR Aruba
diff --git a/share/dictionary.boingo b/share/dictionary.boingo
new file mode 100644 (file)
index 0000000..839dc87
--- /dev/null
@@ -0,0 +1,47 @@
+# -*- text -*-
+# Copyright (C) 2015 The FreeRADIUS Server project and contributors
+#
+##############################################################################
+# Boingo Wi-Fi - http://www.boingo.com/
+#
+# Version:      $Id:
+#
+#               Jorge Pereira <jpereiran@gmail.com>
+#
+##############################################################################
+
+VENDOR         Boingo                          22472
+
+BEGIN-VENDOR   Boingo
+
+ATTRIBUTE      BW-Venue-Id                             7       string
+ATTRIBUTE      BW-Venue-TZ                             8       string
+ATTRIBUTE      BW-Service-Type                         9       string
+ATTRIBUTE      BW-Class                                10      string
+ATTRIBUTE      BW-Venue-Description                    11      string
+ATTRIBUTE      BW-Venue-Price-Type                     12      string
+ATTRIBUTE      BW-Venue-Port-Type                      13      string
+ATTRIBUTE      BW-ISO-Country-Code                     14      string
+ATTRIBUTE      BW-e164-Country-Code                    15      string
+ATTRIBUTE      BW-State-Name                           16      string
+ATTRIBUTE      BW-City-Name                            17      string
+ATTRIBUTE      BW-Area-Code                            18      integer
+ATTRIBUTE      CL-Brand                                19      string
+ATTRIBUTE      CL-Software-Version                     20      string
+ATTRIBUTE      CL-Reg-Number                           21      string
+ATTRIBUTE      CL-Method-Version                       22      string
+ATTRIBUTE      CL-Token-Version                        23      string
+ATTRIBUTE      CL-APDB-Version                         24      string
+ATTRIBUTE      CL-User-Agent                           25      string
+ATTRIBUTE      CL-SSC                                  26      string
+ATTRIBUTE      BW-User-Group                           27      string
+ATTRIBUTE      BW-Venue-Name                           29      string
+ATTRIBUTE      BW-Category                             30      string
+ATTRIBUTE      BW-User-Role                            32      string
+ATTRIBUTE      BW-User-Name                            33      string
+ATTRIBUTE      BW-User-Password                        34      string
+ATTRIBUTE      BW-User-Prefix                          35      string
+ATTRIBUTE      BW-User-Realm                           36      string
+ATTRIBUTE      BW-Operator-Name                        37      string
+
+END-VENDOR      Boingo
index 9756fe9..5b6ff35 100644 (file)
@@ -13,10 +13,10 @@ 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-Primary-DNS                         5       ipaddr
+ATTRIBUTE      ASA-Secondary-DNS                       6       ipaddr
+ATTRIBUTE      ASA-Primary-WINS                        7       ipaddr
+ATTRIBUTE      ASA-Secondary-WINS                      8       ipaddr
 ATTRIBUTE      ASA-SEP-Card-Assignment                 9       integer
 ATTRIBUTE      ASA-Tunneling-Protocols                 11      integer
 ATTRIBUTE      ASA-IPsec-Sec-Association               12      string
@@ -54,9 +54,9 @@ 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-DHCP-Network-Scope                  61      ipaddr
 ATTRIBUTE      ASA-Intercept-DHCP-Configure-Msg        62      integer
-ATTRIBUTE      ASA-MS-Client-Subnet-Mask               63      integer
+ATTRIBUTE      ASA-MS-Client-Subnet-Mask               63      ipaddr
 ATTRIBUTE      ASA-Allow-Network-Extension-Mode        64      integer
 ATTRIBUTE      ASA-Authorization-Type                  65      integer
 ATTRIBUTE      ASA-Authorization-Required              66      integer
index 6634679..cf79ae1 100644 (file)
@@ -225,7 +225,7 @@ ATTRIBUTE   DHCP-Service-Scope                      79      octets
 # Rapid Commit
 ATTRIBUTE      DHCP-Rapid-Commit                       80      octets
 # Fully Qualified Domain Name
-ATTRIBUTE      DHCP-Client-FQDN                        81      string
+ATTRIBUTE      DHCP-Client-FQDN                        81      octets
 # Relay Agent Information
 ATTRIBUTE      DHCP-Relay-Agent-Information            82      tlv
 
@@ -245,7 +245,12 @@ ATTRIBUTE  DHCP-RADIUS-Attributes                  82.7    octets
 
 # Horribly complicated
 ATTRIBUTE      DHCP-Authentication-Information         82.8    octets
-ATTRIBUTE      DHCP-Vendor-Specific-Information        82.9    vsa
+
+#
+#  We'll fix this later
+#
+ATTRIBUTE      DHCP-Vendor-Specific-Information        82.9    octets
+
 ATTRIBUTE      DHCP-Relay-Agent-Flags                  82.10   byte
 ATTRIBUTE      DHCP-Server-Identifier-Override         82.11   ipaddr
 
index 1344cf8..c3221ff 100644 (file)
@@ -80,6 +80,7 @@ ATTRIBUTE     Tunnel-DNIS                             37      integer
 
 VALUE  Tunnel-DNIS                     DNIS                    1
 VALUE  Tunnel-DNIS                     DNIS-Only               2
+VALUE  Tunnel-DNIS                     DNIS-Generate           4
 
 ATTRIBUTE      Medium-Type                             38      integer
 
@@ -422,10 +423,8 @@ ATTRIBUTE  Acct-Alt-Session-ID                     197     string
 ATTRIBUTE      Idle-Timeout-Threshold                  198     integer
 ATTRIBUTE      Double-Authentication                   199     integer
 ATTRIBUTE      SBC-Adjacency                           200     string
-#binary in radiator
-ATTRIBUTE      DHCP-Field                              201     string
-#binary in radiator
-ATTRIBUTE      DHCP-Option                             202     string
+ATTRIBUTE      DHCP-Field                              201     octets
+ATTRIBUTE      DHCP-Option                             202     octets
 ATTRIBUTE      Security-Service                        203     string
 ATTRIBUTE      Reauth-Service-Name                     204     string has_tag
 ATTRIBUTE      Flow-IP-Profile                         205     string
@@ -436,5 +435,17 @@ ATTRIBUTE  Cluster-Partition-ID                    209     string
 ATTRIBUTE      Circuit-Group-Member                    210     string
 ATTRIBUTE      Delegated-Max-Prefix                    212     integer
 ATTRIBUTE      IPv4-Address-Release-Control            213     string
+ATTRIBUTE      Acct-Input-IPv4-Octets                  214     integer
+ATTRIBUTE      Acct-Output-IPv4-Octets                 215     integer
+ATTRIBUTE      Acct-Input-IPv4-Packets                 216     integer
+ATTRIBUTE      Acct-Output-IPv4-Packets                217     integer
+ATTRIBUTE      Acct-Input-IPv4-Gigawords               218     integer
+ATTRIBUTE      Acct-Output-IPv4-Gigawords              219     integer
+ATTRIBUTE      Acct-Input-IPv6-Octets                  220     integer
+ATTRIBUTE      Acct-Output-IPv6-Octets                 221     integer
+ATTRIBUTE      Acct-Input-IPv6-Packets                 222     integer
+ATTRIBUTE      Acct-Output-IPv6-Packets                223     integer
+ATTRIBUTE      Acct-Input-IPv6-Gigawords               224     integer
+ATTRIBUTE      Acct-Output-IPv6-Gigawords              225     integer
 
 END-VENDOR     Ericsson-AB
index 0e07ead..93e1443 100644 (file)
@@ -83,7 +83,7 @@ ATTRIBUTE     Replicate-To-Realm                      1049    string
 ATTRIBUTE      Acct-Session-Start-Time                 1050    date
 ATTRIBUTE      Acct-Unique-Session-Id                  1051    string
 ATTRIBUTE      Client-IP-Address                       1052    ipaddr virtual
-ATTRIBUTE      Ldap-UserDn                             1053    string
+ATTRIBUTE      LDAP-UserDN                             1053    string
 ATTRIBUTE      NS-MTA-MD5-Password                     1054    string
 ATTRIBUTE      SQL-User-Name                           1055    string
 ATTRIBUTE      LM-Password                             1057    octets
@@ -102,12 +102,12 @@ ATTRIBUTE Digest-CNonce                           1070    string
 ATTRIBUTE      Digest-Nonce-Count                      1071    string
 ATTRIBUTE      Digest-User-Name                        1072    string
 ATTRIBUTE      Pool-Name                               1073    string
-ATTRIBUTE      Ldap-Group                              1074    string
+# LDAP-Group is now dynamically created
 ATTRIBUTE      Module-Success-Message                  1075    string
 ATTRIBUTE      Module-Failure-Message                  1076    string
 #              X99-Fast                1077    integer
 ATTRIBUTE      Rewrite-Rule                            1078    string
-ATTRIBUTE      Sql-Group                               1079    string
+# SQL-Group is now dynamically created
 ATTRIBUTE      Response-Packet-Type                    1080    integer virtual
 ATTRIBUTE      Digest-HA1                              1081    string
 ATTRIBUTE      MS-CHAP-Use-NTLM-Auth                   1082    integer
@@ -177,6 +177,9 @@ ATTRIBUTE   Packet-Original-Timestamp               1109    date
 ATTRIBUTE      SQL-Table-Name                          1110    string
 ATTRIBUTE      Home-Server-Pool                        1111    string
 
+# For delayed evaluation of maps
+ATTRIBUTE      Attribute-Map                           1112    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...
@@ -592,14 +595,12 @@ ATTRIBUTE Radclient-Test-Name                     2200    string
 #      Non-Protocol Integer Translations
 #
 
-VALUE  Auth-Type                       Local                   0
-VALUE  Auth-Type                       System                  1
-VALUE  Auth-Type                       SecurID                 2
-VALUE  Auth-Type                       Crypt-Local             3
+VALUE  Auth-Type                       Local                   1
+VALUE  Auth-Type                       System                  2
+VALUE  Auth-Type                       SecurID                 3
 VALUE  Auth-Type                       Reject                  4
 VALUE  Auth-Type                       ActivCard               5
 VALUE  Auth-Type                       EAP                     6
-VALUE  Auth-Type                       ARAP                    7
 
 #
 #      FreeRADIUS extensions (most originally from Cistron)
@@ -623,31 +624,31 @@ VALUE     Auth-Type                       MS-CHAP-V2              1034
 #
 #      Authorization type, too.
 #
-VALUE  Autz-Type                       Local                   0
+VALUE  Autz-Type                       Local                   1
 
 #
 #      And accounting
 #
-VALUE  Acct-Type                       Local                   0
+VALUE  Acct-Type                       Local                   1
 
 #
 #      And Session handling
 #
-VALUE  Session-Type                    Local                   0
+VALUE  Session-Type                    Local                   1
 
 #
 #      And Post-Auth
-VALUE  Post-Auth-Type                  Local                   0
-VALUE  Post-Auth-Type                  Reject                  1
-VALUE  Post-Auth-Type                  Challenge               2
+VALUE  Post-Auth-Type                  Local                   1
+VALUE  Post-Auth-Type                  Reject                  2
+VALUE  Post-Auth-Type                  Challenge               3
 
 #
 #      And Post-Proxy
-VALUE  Post-Proxy-Type                 Fail                    0
-VALUE  Post-Proxy-Type                 Fail-Authentication     1
-VALUE  Post-Proxy-Type                 Fail-Accounting         2
-VALUE  Post-Proxy-Type                 Fail-CoA                3
-VALUE  Post-Proxy-Type                 Fail-Disconnect         4
+VALUE  Post-Proxy-Type                 Fail                    1
+VALUE  Post-Proxy-Type                 Fail-Authentication     2
+VALUE  Post-Proxy-Type                 Fail-Accounting         3
+VALUE  Post-Proxy-Type                 Fail-CoA                4
+VALUE  Post-Proxy-Type                 Fail-Disconnect         5
 
 #
 #      Experimental Non-Protocol Integer Translations for FreeRADIUS
index 87c54ef..18d3abc 100644 (file)
@@ -32,7 +32,7 @@ VALUE MS-MPPE-Encryption-Types        RC4-40or128-bit-Allowed 6
 ATTRIBUTE      MS-RAS-Vendor                           9       integer # content is Vendor-ID
 ATTRIBUTE      MS-CHAP-Domain                          10      string
 ATTRIBUTE      MS-CHAP-Challenge                       11      octets
-ATTRIBUTE      MS-CHAP-MPPE-Keys                       12      octets  encrypt=1
+ATTRIBUTE      MS-CHAP-MPPE-Keys                       12      octets[24]  encrypt=1
 ATTRIBUTE      MS-BAP-Usage                            13      integer
 ATTRIBUTE      MS-Link-Utilization-Threshold           14      integer # values are 1-100
 ATTRIBUTE      MS-Link-Drop-Time-Limit                 15      integer
index 22f0b13..59775bb 100644 (file)
@@ -41,11 +41,20 @@ ATTRIBUTE   Mikrotik-Address-List                   19      string
 ATTRIBUTE      Mikrotik-Wireless-MPKey                 20      string
 ATTRIBUTE      Mikrotik-Wireless-Comment               21      string
 ATTRIBUTE      Mikrotik-Delegated-IPv6-Pool            22      string
+ATTRIBUTE      Mikrotik-DHCP-Option-Set                23      string
+ATTRIBUTE      Mikrotik-DHCP-Option-Param-STR1         24      string
+ATTRIBUTE      Mikortik-DHCP-Option-ParamSTR2          25      string
+ATTRIBUTE      Mikrotik-Wireless-VLANID                26      integer
+ATTRIBUTE      Mikrotik-Wireless-VLANID-Type           27      integer
+ATTRIBUTE      Mikrotik-Wireless-Minsignal             28      string
+ATTRIBUTE      Mikrotik-Wireless-Maxsignal             29      string
 
 # MikroTik Values
 
 VALUE  Mikrotik-Wireless-Enc-Algo      No-encryption           0
 VALUE  Mikrotik-Wireless-Enc-Algo      40-bit-WEP              1
 VALUE  Mikrotik-Wireless-Enc-Algo      104-bit-WEP             2
+VALUE  Mikrotik-Wireless-Enc-Algo      AES-CCM                 3
+VALUE  Mikrotik-Wireless-Enc-Algo      TKIP                    4
 
 END-VENDOR     Mikrotik
index ad54f7a..9948cdc 100644 (file)
@@ -30,4 +30,6 @@ ATTRIBUTE     Motorola-WiMAX-Convergence-Sublayer     63      octets
 ATTRIBUTE      Motorola-WiMAX-Service-Flows            64      string
 ATTRIBUTE      Motorola-WiMAX-VLAN-ID                  65      octets
 
+ATTRIBUTE      Motorola-Accounting-Message             80      string
+
 END-VENDOR    Motorola
index f209954..dd48edc 100644 (file)
@@ -7,5 +7,5 @@
 
 # The Value field contains two octets (00 - 99).  ANSI T1.113 and
 # BELLCORE 394 can be used for additional information about these
-# values and their use. 
+# values and their use.
 ATTRIBUTE      Originating-Line-Info                   94      octets[2]
diff --git a/share/dictionary.rfc7499 b/share/dictionary.rfc7499
new file mode 100644 (file)
index 0000000..47cfd30
--- /dev/null
@@ -0,0 +1,17 @@
+# -*- text -*-
+# Copyright (C) 2015 The FreeRADIUS Server project and contributors
+#
+#      Attributes and values defined in RFC 7499.
+#      http://www.ietf.org/rfc/rfc7499.txt
+#
+#      $Id$
+#
+
+ATTRIBUTE      Frag-Status                             241.1   integer
+ATTRIBUTE      Proxy-State-Length                      241.2   integer
+
+VALUE  Frag-Status                     Reserved                0
+VALUE  Frag-Status                     Fragmentation-Supported 1
+VALUE  Frag-Status                     More-Data-Pending       2
+VALUE  Frag-Status                     More-Data-Request       3
+
diff --git a/share/dictionary.wifialliance b/share/dictionary.wifialliance
new file mode 100644 (file)
index 0000000..124f062
--- /dev/null
@@ -0,0 +1,84 @@
+# -*- text -*-
+# Copyright (C) 2015 The FreeRADIUS Server project and contributors
+##############################################################################
+#
+#       WiFi-Alliance attributes for Hotspot 2.0
+#
+#       $Id$
+#
+##############################################################################
+
+VENDOR         WiFi-Alliance                   40808
+
+BEGIN-VENDOR   WiFi-Alliance
+
+#  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
+#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#  | Server Method | Subscription Remediation Server URL
+#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#
+#  Server Method
+#
+#      0 = OMA DM
+#      1 = Soap XML SPP
+#      2..255 reserved
+#
+ATTRIBUTE      HS20-Subscription-Remediation-Needed    1       octets
+ATTRIBUTE      HS20-AP-Version                         2       byte
+
+#  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
+#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#  |    Version    |    PPS MO UpdateIdentifier    |
+#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#
+#  Version
+#
+#      0 = release 1
+#      1 = release 2
+#
+ATTRIBUTE      HS20-Mobile-Device-Version              3       octets
+
+#  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     |        Re-auth Delay          |     URL
+#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#
+#  Code
+#
+#      0 = User's subscription does not allow or no longer allows access at this BSS
+#      1 = User's subscription does not allow or no longer allows access at this ESS
+#
+#
+#  Re-Auth delay = delay in seconds that a mobile device waits before attempting
+#                 reassociation.
+#                 0 == delay is decided by the mobile device.
+#
+#  URL = UTF-8 encoded URL.
+#      Which provides a webpage explaining why the mobile device was
+#      not authorized (or is no longer authorized)
+#
+ATTRIBUTE      HS20-Deauthentication-Request           4       octets
+
+#  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
+#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#  |       SWT     |   Session Information URL
+#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+#
+#  SWT = Session Warning Time is the number of minutes of advance
+#  notice an AP shall provide to the mobile device before terminating
+#  its session. When SWT is set to the special value of 255, the AP
+#  (802.1X authenticator) chooses the session warning time value.
+#
+#  Session Information URL field: URL which is transmitted to a mobile
+#  device in a BSS Transition Management Request frame SWT minutes
+#  before the mobile device’s session is terminated. The URL provides
+#  the location of a webpage with information for the user on how to
+#  extend the session.
+#
+ATTRIBUTE      HS20-Session-Information-URL            5       octets
+
+END-VENDOR    WiFi-Alliance
index 8105511..daa2d58 100755 (executable)
@@ -2,7 +2,7 @@
 #
 #  Format the dictionaries according to a standard scheme.
 #
-#  Usage: cat dictionary | ./format.pl > new
+#  Usage: ./format.pl dictionary.foo
 #
 #  We don't over-write the dictionaries in place, so that the process
 #  can be double-checked by hand.
@@ -71,7 +71,7 @@ while (@ARGV) {
        #
        #  Remember the vendor
        #
-       if (/^VENDOR\s+([\w-]+)\s+(\w+)(.*)/) {
+       if (/^VENDOR\s+([-\w]+)\s+(\w+)(.*)/) {
            $name=$1;
            $len = length $name;
            if ($len < 32) {
@@ -91,7 +91,7 @@ while (@ARGV) {
        #
        #  Remember if we did begin-vendor.
        #
-       if (/^BEGIN-VENDOR\s+([\w-]+)/) {
+       if (/^BEGIN-VENDOR\s+([-\w]+)/) {
            $begin_vendor = 1;
            if (!defined $vendor) {
                $vendor = $1;
@@ -106,7 +106,7 @@ while (@ARGV) {
        #
        #  Get attribute.
        #
-       if (/^ATTRIBUTE\s+([\w-]+)\s+([\w.]+)\s+(\w+)(.*)/) {
+       if (/^ATTRIBUTE\s+([-\w]+)\s+([\w.]+)\s+(\w+)(.*)/) {
            $name=$1;
            $len = length $name;
            if ($len < 40) {
@@ -146,7 +146,7 @@ while (@ARGV) {
        #
        #  Values.
        #
-       if (/^VALUE\s+([\w-]+)\s+([\w-\/,.]+)\s+(\w+)(.*)/) {
+       if (/^VALUE\s+([-\w]+)\s+([-\w\/,.]+)\s+(\w+)(.*)/) {
            $attr=$1;
            $len = length $attr;
            if ($len < 32) {
index 316eed4..c235349 100644 (file)
@@ -9,6 +9,7 @@ missing.h
 tls.h
 features.h
 radpaths.h
+vqp.h
 
 # Build scripts
 build-radpaths-h
index ada10f0..08c5668 100644 (file)
@@ -66,13 +66,15 @@ RFC_HEADERS := $(patsubst share/dictionary.%,src/include/%.h,$(RFC_DICTS))
 
 src/include/attributes.h: share/dictionary.freeradius.internal
        @$(ECHO) HEADER $@
-       @grep ^ATTRIBUTE $<  | awk '{print "PW_"$$2 " " $$3}' | tr '[:lower:]' '[:upper:]' | tr -- - _ | sed 's/^/#define /' > $@
+       @grep ^ATTRIBUTE $<  | awk '{print "PW_"$$2 " " $$3 "   //!< AUTOGENERATED ATTRIBUTE DEFINITION"}' | tr '[:lower:]' '[:upper:]' | tr -- - _ | sed 's/^/#define /' > $@
+       @echo " " >> $@
+       @grep -- 'Auth-Type' $< | grep ^VALUE | awk '{print "PW_"$$2 "_" $$3 " " $$4 "  //!< AUTOGENERATED VALUE DEFINITION"}' | tr '[:lower:]' '[:upper:]' | tr -- - _ | sed 's/^/#define /' >> $@
 
-src/include/%.h: share/dictionary.%
+src/include/%.h: share/dictionary.% share/dictionary.vqp
        @$(ECHO) HEADER $@
-       @grep ^ATTRIBUTE $<  | awk '{print "PW_"$$2 " " $$3}' | tr '[:lower:]' '[:upper:]' | tr -- - _ | sed 's/^/#define /' > $@
+       @grep ^ATTRIBUTE $<  | awk '{print "PW_"$$2 " " $$3 "   //!< AUTOGENERATED ATTRIBUTE DEFINITION"}' | tr '[:lower:]' '[:upper:]' | tr -- - _ | sed 's/^/#define /' > $@
 
-src/include/radius.h: | src/include/attributes.h $(RFC_HEADERS)
+src/include/radius.h: | src/include/attributes.h $(RFC_HEADERS) src/include/vqp.h
 
 #
 #  So the headers are created before we compile anything
index f4146b2..eab1227 100644 (file)
 /* Define to 1 to have OpenSSL version check enabled */
 #undef ENABLE_OPENSSL_VERSION_CHECK
 
+/* Define if your processor stores words with the most significant byte first
+   */
+#undef FR_BIG_ENDIAN
+
+/* Define if your processor stores words with the least significant byte first
+   */
+#undef FR_LITTLE_ENDIAN
+
 /* style of gethostbyaddr_r functions */
 #undef GETHOSTBYADDRRSTYLE
 
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #undef HAVE_ARPA_INET_H
 
+/* Define if your compiler supports the __bounded__ attribute (usually OpenBSD
+   gcc). */
+#undef HAVE_ATTRIBUTE_BOUNDED
+
 /* Define to 1 if you have the `bindat' function. */
 #undef HAVE_BINDAT
 
 /* Define if we have a binary safe regular expression library */
 #undef HAVE_BINSAFE_REGEX
 
-/* Define if the compiler supports __builtin_types_compatible_p */
+/* Define if the compiler supports __builtin_bswap64 */
 #undef HAVE_BUILTIN_BSWAP_64
 
 /* Define if the compiler supports __builtin_choose_expr */
@@ -65,6 +77,9 @@
    */
 #undef HAVE_DIRENT_H
 
+/* Define to 1 if you have the `dladdr' function. */
+#undef HAVE_DLADDR
+
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
 
 /* Raw version string from VERSION file */
 #undef RADIUSD_VERSION_STRING
 
-/* Define if your processor stores words with the most significant byte first
-   */
-#undef RADIUS_BIG_ENDIAN
-
-/* Define if your processor stores words with the least significant byte first
-   */
-#undef RADIUS_LITTLE_ENDIAN
-
 /* Define as the return type of signal handlers (`int' or `void'). */
 #undef RETSIGTYPE
 
index e12d457..ca3e0ca 100644 (file)
@@ -29,7 +29,7 @@ RCSIDH(base64_h, "$Id$")
 #define FR_BASE64_ENC_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
 #define FR_BASE64_DEC_LENGTH(inlen) ((3 * (inlen / 4)) + 2)
 
-int fr_isbase64(char c);
+bool fr_is_base64(char c);
 
 size_t fr_base64_encode(char *out, size_t outlen, uint8_t const *in, size_t inlen);
 
index 7933230..2d80039 100644 (file)
@@ -51,6 +51,12 @@ extern "C" {
 #  define CC_HINT(_x)
 #endif
 
+#ifdef HAVE_ATTRIBUTE_BOUNDED
+#  define CC_BOUNDED(_x, ...) CC_HINT(__bounded__(_x, ## __VA_ARGS__))
+#else
+#  define CC_BOUNDED(...)
+#endif
+
 /*
  *     Macros to add pragmas
  */
@@ -116,16 +122,16 @@ extern "C" {
  *     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
+ *     Here at least the endianness can be set explicitly with
  *     -DLITTLE_ENDIAN or -DBIG_ENDIAN.
  */
-#if !defined(RADIUS_LITTLE_ENDIAN) && !defined(RADIUS_BIG_ENDIAN)
+#if !defined(FR_LITTLE_ENDIAN) && !defined(FR_BIG_ENDIAN)
 #  if defined(__LITTLE_ENDIAN__) || \
       (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
-#    define RADIUS_LITTLE_ENDIAN 1
+#    define FR_LITTLE_ENDIAN 1
 #  elif defined(__BIG_ENDIAN__) || \
       (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
-#    define RADIUS_BIG_ENDIAN 1
+#    define FR_BIG_ENDIAN 1
 #  else
 #    error Failed determining endianness of system
 #  endif
diff --git a/src/include/clients.h b/src/include/clients.h
new file mode 100644 (file)
index 0000000..ea57aae
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   the Free Software Foundation; either version 2 of the License, or (at
+ *   your option) any later version. either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#ifndef CLIENTS_H
+#define CLIENTS_H
+/*
+ * $Id$
+ *
+ * @file clients.h
+ * @brief Function declarations and structures to manage clients.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2015 The FreeRADIUS server project
+ */
+
+/** Describes a host allowed to send packets to the server
+ *
+ */
+typedef struct radclient {
+       fr_ipaddr_t             ipaddr;                 //!< IPv4/IPv6 address of the host.
+       fr_ipaddr_t             src_ipaddr;             //!< IPv4/IPv6 address to send responses
+                                                       //!< from (family must match ipaddr).
+
+       char const              *longname;              //!< Client identifier.
+       char const              *shortname;             //!< Client nickname.
+
+       char const              *secret;                //!< Secret PSK.
+
+       bool                    message_authenticator;  //!< Require RADIUS message authenticator in requests.
+
+       char const              *nas_type;              //!< Type of client (arbitrary).
+
+       char const              *login;                 //!< Username to use for simultaneous use checks.
+       char const              *password;              //!< Password to use for simultaneous use checks.
+
+       char const              *server;                //!< Virtual server client is associated with.
+
+       int                     number;                 //!< Unique client number.
+
+       CONF_SECTION            *cs;                    //!< CONF_SECTION that was parsed to generate the client.
+
+#ifdef WITH_STATS
+       fr_stats_t              auth;                   //!< Authentication stats.
+#  ifdef WITH_ACCOUNTING
+       fr_stats_t              acct;                   //!< Accounting stats.
+#  endif
+#  ifdef WITH_COA
+       fr_stats_t              coa;                    //!< Change of Authorization stats.
+       fr_stats_t              dsc;                    //!< Disconnect-Request stats.
+#  endif
+#endif
+
+       struct timeval          response_window;        //!< How long the client has to respond.
+
+       int                     proto;                  //!< Protocol number.
+#ifdef WITH_TCP
+       fr_socket_limit_t       limit;                  //!< Connections per client (TCP clients only).
+#endif
+#ifdef WITH_TLS
+       bool                    tls_required;           //!< whether TLS encryption is required.
+#endif
+
+#ifdef WITH_DYNAMIC_CLIENTS
+       uint32_t                lifetime;               //!< How long before the client is removed.
+       uint32_t                dynamic;                //!< Whether the client was dynamically defined.
+       time_t                  created;                //!< When the client was created.
+
+       time_t                  last_new_client;        //!< Used for relate limiting addition and deletion of
+                                                       //!< dynamic clients.
+
+       char const              *client_server;         //!< Virtual server associated with this dynamic client.
+                                                       //!< Only used where client specifies a network of potential
+                                                       //!< clients.
+
+       bool                    rate_limit;             //!< Where addition of clients should be rate limited.
+#endif
+
+#ifdef WITH_COA
+       char const              *coa_name;              //!< Name of the CoA home server or pool.
+       home_server_t           *coa_server;            //!< The CoA home_server_t the client is associated with.
+                                                       //!< Must be used exclusively from coa_pool.
+       home_pool_t             *coa_pool;              //!< The CoA home_pool_t the client is associated with.
+                                                       //!< Must be used exclusively from coa_server.
+       bool                    defines_coa_server;     //!< Client also defines a home_server.
+#endif
+} RADCLIENT;
+
+typedef struct radclient_list RADCLIENT_LIST;
+
+/** Callback for retrieving values when building client sections
+ *
+ * Example:
+ @code{.c}
+   int _client_value_cb(char **out, CONF_PAIR const *cp, void *data)
+   {
+       my_result *result = data;
+       char *value;
+
+       value = get_attribute_from_result(result, cf_pair_value(cp));
+       if (!value) {
+               *out = NULL;
+               return 0;
+       }
+
+       *out = talloc_strdup(value);
+       free_attribute(value);
+
+       if (!*out) return -1;
+       return 0;
+   }
+ @endcode
+ *
+ * @param[out] out Where to write a pointer to the talloced value buffer.
+ * @param[in] cp The value of the CONF_PAIR specifies the attribute name to retrieve from the result.
+ * @param[in] data Pointer to the result struct to copy values from.
+ * @return 0 on success -1 on failure.
+ */
+typedef int (*client_value_cb_t)(char **out, CONF_PAIR const *cp, void *data);
+
+RADCLIENT_LIST *client_list_init(CONF_SECTION *cs);
+
+void           client_list_free(RADCLIENT_LIST *clients);
+
+RADCLIENT_LIST *client_list_parse_section(CONF_SECTION *section, bool tls_required);
+
+void           client_free(RADCLIENT *client);
+
+bool           client_add(RADCLIENT_LIST *clients, RADCLIENT *client);
+
+#ifdef WITH_DYNAMIC_CLIENTS
+void           client_delete(RADCLIENT_LIST *clients, RADCLIENT *client);
+
+RADCLIENT      *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request);
+#endif
+
+int            client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data);
+
+RADCLIENT      *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bool with_coa);
+
+RADCLIENT      *client_afrom_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);
+
+RADCLIENT      *client_findbynumber(RADCLIENT_LIST const *clients, int number);
+
+RADCLIENT      *client_find_old(fr_ipaddr_t const *ipaddr);
+
+bool           client_add_dynamic(RADCLIENT_LIST *clients, RADCLIENT *master, RADCLIENT *c);
+
+RADCLIENT      *client_read(char const *filename, int in_server, int flag);
+#endif /* CLIENTS_H */
index 0396bd7..62d16b0 100644 (file)
@@ -21,14 +21,16 @@ extern "C" {
 /*
  * Export the minimum amount of information about these structs
  */
-typedef struct conf_item CONF_ITEM;
-typedef struct conf_pair CONF_PAIR;
-typedef struct conf_part CONF_SECTION;
-typedef struct conf_data CONF_DATA;
+typedef struct conf_item CONF_ITEM;    //!< Generic configuration element, extended to become
+                                       ///< a #CONF_PAIR, a #CONF_SECTION or #CONF_DATA.
+typedef struct conf_pair CONF_PAIR;    //!< #CONF_ITEM with an attribute, an operator and a value.
+typedef struct conf_part CONF_SECTION; //!< #CONF_ITEM used to group multiple #CONF_PAIR and #CONF_SECTION, together.
+typedef struct conf_data CONF_DATA;    //!< #CONF_ITEM used to associate arbitrary data
+                                       ///< with a #CONF_PAIR or #CONF_SECTION.
 
 
-typedef void conf_type_mismatch;
-typedef void conf_type_invalid;
+typedef void conf_type_mismatch;       //!< Dummy type used to indicate PW_TYPE_*/C type mismatch.
+typedef void conf_type_invalid;                //!< Dummy type used to indicate invalid PW_TYPE_*.
 
 #if defined(HAVE_BUILTIN_CHOOSE_EXPR) && defined(HAVE_BUILTIN_TYPES_COMPATIBLE_P)
 /*
@@ -37,19 +39,29 @@ typedef void conf_type_invalid;
  */
 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.
+/** Check the type #_t matches the destination data type
+ *
+ * Validation macro to check the type of the pointer or offset #_p passed in
+ * matches the type #_t of the configuration item.
+ *
+ * Uses various magic builtin precompilation functions, so will likely only
+ * work with recent versions of clang and gcc.
+ *
+ * @note The warnings/errors emitted are usually awful.
+ *
+ * @param _t a #PW_TYPE value with optional PW_TYPE_* flags.
+ * @param _ct data type of global or struct field, obtained with ``__typeof__``.
+ * @param _p Pointer or offset.
  */
 #  define FR_CONF_TYPE_CHECK(_t, _ct, _p) \
        __builtin_choose_expr((_t & PW_TYPE_TMPL),\
-               __builtin_choose_expr(__builtin_types_compatible_p(value_pair_tmpl_t **, _ct), _p, (conf_type_mismatch) 0),\
+               __builtin_choose_expr(__builtin_types_compatible_p(vp_tmpl_t **, _ct), _p, (conf_type_mismatch) 0),\
        __builtin_choose_expr((((_t) & 0xff) == PW_TYPE_STRING),\
                __builtin_choose_expr(__builtin_types_compatible_p(char const **, _ct), _p, (conf_type_mismatch) 0),\
        __builtin_choose_expr((((_t) & 0xff) == PW_TYPE_BOOLEAN),\
                __builtin_choose_expr(__builtin_types_compatible_p(bool *, _ct), _p, (conf_type_mismatch) 0),\
        __builtin_choose_expr((((_t) & 0xff) == PW_TYPE_SUBSECTION),\
-               NULL,\
+               _p,\
        __builtin_choose_expr((((_t) & 0xff) == PW_TYPE_INTEGER),\
                __builtin_choose_expr(__builtin_types_compatible_p(uint32_t *, _ct), _p, (conf_type_mismatch) 0),\
        __builtin_choose_expr((((_t) & 0xff) == PW_TYPE_IPV4_ADDR),\
@@ -96,6 +108,8 @@ typedef struct timeval _timeval_t;
 #  define FR_ITEM_POINTER(_t, _p)      _t, _p
 #endif
 
+#define FR_CONF_DEPRECATED(_t, _p, _f) (_t) | PW_TYPE_DEPRECATED, 0, NULL
+
 /*
  *  Instead of putting the information into a configuration structure,
  *  the configuration file routines MAY just parse it directly into
@@ -103,29 +117,29 @@ typedef struct timeval _timeval_t;
  */
 #define PW_TYPE_SUBSECTION     102
 
-/*
- * Configuration type flags, these modify the processing of config
- * items.
+/** @name #CONF_PARSER type flags
+ *
+ * These flags should be or'd with another PW_TYPE_* value to create validation
+ * rules for the #cf_item_parse function.
+ *
+ * @note File PW_TYPE_FILE_* types have a base type of string, so they're validated
+ *      correctly by the config parser.
+ * @{
  */
-#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.
+#define PW_TYPE_DEPRECATED     (1 << 10) //!< If a matching #CONF_PAIR is found, error out with a deprecated message.
+#define PW_TYPE_REQUIRED       (1 << 11) //!< Error out if no matching #CONF_PAIR is found, and no dflt value is set.
+#define PW_TYPE_ATTRIBUTE      (1 << 12) //!< Value must resolve to attribute in dict (deprecated, use #PW_TYPE_TMPL).
+#define PW_TYPE_SECRET         (1 << 13) //!< Only print value if debug level >= 3.
 
-/*
- * 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 PW_TYPE_FILE_INPUT     ((1 << 14) | PW_TYPE_STRING) //!< File matching value must exist, and must be readable.
+#define PW_TYPE_FILE_OUTPUT    ((1 << 15) | PW_TYPE_STRING) //!< File matching value must exist, and must be writeable.
 
-#define PW_TYPE_XLAT           (1 << 16)       //!< string will be dynamically expanded.
-#define PW_TYPE_TMPL           (1 << 17)       //!< CONF_PAIR should be parsed as a template.
+#define PW_TYPE_XLAT           (1 << 16) //!< string will be dynamically expanded.
+#define PW_TYPE_TMPL           (1 << 17) //!< CONF_PAIR should be parsed as a template.
 
-#define PW_TYPE_MULTI          (1 << 18)       //!< CONF_PAIR can have multiple copies.
-#define PW_TYPE_NOT_EMPTY      (1 << 19)       //!< CONF_PAIR is required to have a non zero length value.
+#define PW_TYPE_MULTI          (1 << 18) //!< CONF_PAIR can have multiple copies.
+#define PW_TYPE_NOT_EMPTY      (1 << 19) //!< CONF_PAIR is required to have a non zero length value.
+/* @} **/
 
 #define FR_INTEGER_COND_CHECK(_name, _var, _cond, _new)\
 do {\
@@ -148,14 +162,59 @@ do {\
        }\
 } while (0)
 
+#define FR_TIMEVAL_TO_MS(_x) (((_x)->tv_usec * 1000) + ((_x)->tv_sec / 1000))
+extern bool                    check_config;
+
+/** Defines a #CONF_PAIR to C data type mapping
+ *
+ * Is typically used to define mappings between module sections, and module instance structs.
+ * May also be used to set global configuration options.
+ *
+ * Offset/data values should be set using #FR_CONF_OFFSET or #FR_CONF_POINTER.
+ *
+ * Example with #FR_CONF_OFFSET :
+ @code{.c}
+   static CONF_PARSER module_config[] = {
+       { "example", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_NOT_EMPTY, example_instance_t, example), "default_value" },
+       CONF_PARSER_TERMINATOR
+   }
+ @endcode
+ *
+ * Example with #FR_CONF_POINTER :
+ @code{.c}
+   static CONF_PARSER global_config[] = {
+       { "example", FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_NOT_EMPTY, &my_global), "default_value" },
+       CONF_PARSER_TERMINATOR
+   }
+ @endcode
+ *
+ * @see FR_CONF_OFFSET
+ * @see FR_CONF_POINTER
+ * @see cf_section_parse
+ * @see cf_item_parse
+ */
 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.
+       char const      *name;                  //!< Name of the #CONF_ITEM to parse.
+       int             type;                   //!< A #PW_TYPE value, may be or'd with one or more PW_TYPE_* flags.
+                                               //!< @see cf_item_parse.
+
+       size_t          offset;                 //!< Relative offset of field or structure to write the parsed value to.
+                                               //!< When #type is set to #PW_TYPE_SUBSECTION, may be used to specify
+                                               //!< a base offset to add to all offsets contained within the
+                                               //!< subsection.
+                                               //!< @note Must be used exclusively to #data.
+
+       void            *data;                  //!< Pointer to a static variable to write the parsed value to.
+                                               //!< @note Must be used exclusively to #offset.
+
        const void      *dflt;                  //!< Default as it would appear in radiusd.conf.
+                                               //!< When #type is set to #PW_TYPE_SUBSECTION, should be a pointer
+                                               //!< to the start of another array of #CONF_PARSER structs, forming
+                                               //!< the subsection.
 } CONF_PARSER;
 
+#define CONF_PARSER_TERMINATOR { NULL, -1, 0, NULL, NULL }
+
 CONF_PAIR      *cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value,
                               FR_TOKEN op, FR_TOKEN lhs_type, FR_TOKEN rhs_type);
 CONF_PAIR      *cf_pair_dup(CONF_SECTION *parent, CONF_PAIR *cp);
@@ -172,7 +231,6 @@ int         cf_section_parse_pass2(CONF_SECTION *, void *base, CONF_PARSER const *varia
 const CONF_PARSER *cf_section_parse_table(CONF_SECTION *cs);
 int            cf_file_read(CONF_SECTION *cs, char const *file);
 void           cf_file_free(CONF_SECTION *cs);
-int            cf_file_include(CONF_SECTION *cs, char const *file);
 
 CONF_PAIR      *cf_pair_find(CONF_SECTION const *, char const *name);
 CONF_PAIR      *cf_pair_find_next(CONF_SECTION const *, CONF_PAIR const *, char const *name);
@@ -230,6 +288,12 @@ CONF_ITEM *cf_reference_item(CONF_SECTION const *parentcs,
                             CONF_SECTION *outercs,
                             char const *ptr);
 
+#define CF_FILE_NONE   (0)
+#define CF_FILE_ERROR  (1)
+#define CF_FILE_CONFIG (1 << 2)
+#define CF_FILE_MODULE (1 << 3)
+int cf_file_changed(CONF_SECTION *cs, rb_walker_t callback);
+
 extern CONF_SECTION *root_config;
 extern bool cf_new_escape;
 
index 7787d03..422e028 100644 (file)
@@ -70,26 +70,46 @@ typedef void *(*fr_connection_create_t)(TALLOC_CTX *ctx, void *opaque);
  */
 typedef int (*fr_connection_alive_t)(void *opaque, void *connection);
 
-fr_connection_pool_t *fr_connection_pool_module_init(CONF_SECTION *module,
-                                                    void *opaque,
-                                                    fr_connection_create_t c,
-                                                    fr_connection_alive_t a,
-                                                    char const *prefix);
-
-fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
-                                             CONF_SECTION *cs,
-                                             void *opaque,
-                                             fr_connection_create_t c,
-                                             fr_connection_alive_t a,
-                                             char const *log_prefix,
-                                             char const *trigger_prefix);
-void fr_connection_pool_delete(fr_connection_pool_t *pool);
-
-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_del(fr_connection_pool_t *pool, void *conn);
+/*
+ *     Pool allocation/initialisation
+ */
+fr_connection_pool_t   *fr_connection_pool_init(TALLOC_CTX *ctx,
+                                                CONF_SECTION *cs,
+                                                void *opaque,
+                                                fr_connection_create_t c,
+                                                fr_connection_alive_t a,
+                                                char const *log_prefix,
+                                                char const *trigger_prefix);
+
+fr_connection_pool_t   *fr_connection_pool_module_init(CONF_SECTION *module,
+                                                       void *opaque,
+                                                       fr_connection_create_t c,
+                                                       fr_connection_alive_t a,
+                                                       char const *prefix);
+
+fr_connection_pool_t   *fr_connection_pool_copy(TALLOC_CTX *ctx, fr_connection_pool_t *pool, void *opaque);
+
+
+/*
+ *     Pool getters
+ */
+int    fr_connection_pool_get_num(fr_connection_pool_t *pool);
+
+/*
+ *     Pool management
+ */
+void   fr_connection_pool_free(fr_connection_pool_t *pool);
+
+/*
+ *     Connection lifecycle
+ */
+void   *fr_connection_get(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_close(fr_connection_pool_t *pool, void *conn);
 
 #ifdef __cplusplus
 }
index 922978b..d7ca133 100644 (file)
@@ -38,16 +38,19 @@ typedef enum detail_state_t {
 
 typedef struct listen_detail_t {
        fr_event_t      *ev;    /* has to be first entry (ugh) */
+       char const      *name;                  //!< Identifier used in log messages
        int             delay_time;
        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;
@@ -57,8 +60,8 @@ typedef struct listen_detail_t {
 
        off_t           last_offset;
        off_t           timestamp_offset;
-       bool            done_entry;     /* are we done reading this entry? */
-       bool            track;  /* do we track progress through the file? */
+       bool            done_entry;             //!< Are we done reading this entry?
+       bool            track;                  //!< Do we track progress through the file?
 
        uint32_t        load_factor; /* 1..100 */
        uint32_t        poll_interval;
@@ -69,7 +72,6 @@ typedef struct listen_detail_t {
        int             tries;
        bool            one_shot;
        int             outstanding;
-       int             max_outstanding;
        int             has_rtt;
        int             srtt;
        int             rttvar;
index de48a2f..01fa781 100644 (file)
@@ -64,6 +64,7 @@ RADIUS_PACKET *fr_dhcp_recv_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RAD
 #define PW_DHCP_NAK            (1024 + 6)
 #define PW_DHCP_RELEASE                (1024 + 7)
 #define PW_DHCP_INFORM         (1024 + 8)
+#define PW_DHCP_LEASE_QUERY            (1024 + 10)
 
 #define DHCP_MAGIC_VENDOR (54)
 
index bf52a93..ccccfe9 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -36,7 +35,7 @@ extern "C" {
  */
 typedef struct exfile_t exfile_t;
 
-exfile_t *exfile_init(TALLOC_CTX *ctx, uint32_t entries, uint32_t idle);
+exfile_t *exfile_init(TALLOC_CTX *ctx, uint32_t entries, uint32_t idle, bool locking);
 int exfile_open(exfile_t *lf, char const *filename, mode_t permissions, bool append);
 int exfile_close(exfile_t *lf, int fd);
 int exfile_unlock(exfile_t *lf, int fd);
index 8ab5a15..48f46e1 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -112,8 +111,8 @@ typedef void (*sig_t)(int);
  *  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(__FILE__,  __LINE__, _x)
-#  define VERIFY_LIST(_x)      fr_verify_list(__FILE__,  __LINE__, NULL, _x)
+#  define VERIFY_VP(_x)                fr_pair_verify(__FILE__,  __LINE__, _x)
+#  define VERIFY_LIST(_x)      fr_pair_list_verify(__FILE__,  __LINE__, NULL, _x)
 #  define VERIFY_PACKET(_x)    (void) talloc_get_type_abort(_x, RADIUS_PACKET)
 #else
 /*
@@ -136,10 +135,10 @@ typedef void (*sig_t)(int);
 #  define VENDORPEC_USR                429
 #  define VENDORPEC_LUCENT     4846
 #  define VENDORPEC_STARENT    8164
-#  define DEBUG                        if (fr_debug_flag && fr_log_fp) fr_printf_log
+#  define DEBUG                        if (fr_debug_lvl && fr_log_fp) fr_printf_log
 #endif
 
-#  define debug_pair(vp)       do { if (fr_debug_flag && fr_log_fp) { \
+#  define debug_pair(vp)       do { if (fr_debug_lvl && fr_log_fp) { \
                                        vp_print(fr_log_fp, vp); \
                                     } \
                                } while(0)
@@ -188,6 +187,8 @@ typedef struct attr_flags {
 
        unsigned int    virtual : 1;                            //!< for dynamic expansion
 
+       unsigned int    compare : 1;                            //!< has a paircompare registered
+
        uint8_t         encrypt;                                //!< Ecryption method.
        uint8_t         length;
 } ATTR_FLAGS;
@@ -268,7 +269,6 @@ typedef union value_data {
 
        uint8_t                 ipv4prefix[6];                  //!< IPv4 prefix (should be struct?).
 
-       uint8_t                 *tlv;                           //!< Nested TLV (should go away).
        void                    *ptr;                           //!< generic pointer.
 } value_data_t;
 
@@ -363,7 +363,6 @@ typedef struct value_pair_raw {
 #define vp_signed      data.sinteger
 #define vp_integer64   data.integer64
 #define vp_ipv4prefix  data.ipv4prefix
-#define vp_tlv         data.tlv
 #define vp_length      length
 
 typedef struct fr_ipaddr_t {
@@ -430,7 +429,7 @@ int         fr_check_lib_magic(uint64_t magic);
 /*
  *     Printing functions.
  */
-int            fr_utf8_char(uint8_t const *str);
+int            fr_utf8_char(uint8_t const *str, ssize_t inlen);
 char const             *fr_utf8_strchr(int *chr_len, char const *str, char const *chr);
 size_t         fr_prints(char *out, size_t outlen, char const *in, ssize_t inlen, char quote);
 size_t         fr_prints_len(char const *in, ssize_t inlen, char quote);
@@ -438,14 +437,9 @@ char               *fr_aprints(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote);
 
 #define                is_truncated(_ret, _max) ((_ret) >= (_max))
 #define                truncate_len(_ret, _max) (((_ret) >= (_max)) ? ((_max) - 1) : _ret)
-size_t         vp_data_prints_value(char *out, size_t outlen,
-                                    PW_TYPE type, DICT_ATTR const *enumv,
-                                    value_data_t const *data, ssize_t inlen, char quote);
 size_t         vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, char quote);
 
-char           *vp_data_aprints_value(TALLOC_CTX *ctx,
-                                      PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
-                                      size_t inlen, char quote);
+
 char           *vp_aprints_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote);
 
 size_t         vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp);
@@ -508,9 +502,6 @@ DICT_VENDOR *dict_vendorbyvalue(int vendor);
 /*#define dict_valget  dict_valbyattr almost but not quite*/
 #endif
 
-/* md5.c */
-void           fr_md5_calc(uint8_t *, uint8_t const *, unsigned int);
-
 /* radius.c */
 int            rad_send(RADIUS_PACKET *, RADIUS_PACKET const *, char const *secret);
 bool           rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason);
@@ -562,6 +553,12 @@ ssize_t            rad_attr2vp(TALLOC_CTX *ctx,
                            uint8_t const *data, size_t length,
                            VALUE_PAIR **pvp);
 
+ssize_t rad_data2vp_tlvs(TALLOC_CTX *ctx,
+                        RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                        char const *secret, DICT_ATTR const *da,
+                        uint8_t const *start, 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,
@@ -587,15 +584,14 @@ int               rad_vp2attr(RADIUS_PACKET const *packet,
                            VALUE_PAIR const **pvp, uint8_t *ptr, size_t room);
 
 /* pair.c */
-VALUE_PAIR     *pairalloc(TALLOC_CTX *ctx, DICT_ATTR const *da);
-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);
-VALUE_PAIR     *pair_find_by_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);
+VALUE_PAIR     *fr_pair_afrom_da(TALLOC_CTX *ctx, DICT_ATTR const *da);
+VALUE_PAIR     *fr_pair_afrom_num(TALLOC_CTX *ctx, unsigned int attr, unsigned int vendor);
+int            fr_pair_to_unknown(VALUE_PAIR *vp);
+void           fr_pair_list_free(VALUE_PAIR **);
+VALUE_PAIR     *fr_pair_find_by_num(VALUE_PAIR *, unsigned int attr, unsigned int vendor, int8_t tag);
+VALUE_PAIR     *fr_pair_find_by_da(VALUE_PAIR *, DICT_ATTR const *da, int8_t tag);
+
+VALUE_PAIR     *fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR * 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);
@@ -611,47 +607,49 @@ void              fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp);
 void           fr_cursor_merge(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 *a, VALUE_PAIR *b);
-int            pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b);
+void           fr_pair_delete_by_num(VALUE_PAIR **, unsigned int attr, unsigned int vendor, int8_t tag);
+void           fr_pair_add(VALUE_PAIR **, VALUE_PAIR *);
+void           fr_pair_replace(VALUE_PAIR **first, VALUE_PAIR *add);
+int            fr_pair_cmp(VALUE_PAIR *a, VALUE_PAIR *b);
+int            fr_pair_list_cmp(VALUE_PAIR *a, VALUE_PAIR *b);
 
-typedef int8_t (*fr_cmp_t)(void const *a, void const *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     *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from);
-VALUE_PAIR     *paircopy_by_num(TALLOC_CTX *ctx, VALUE_PAIR *from, unsigned int attr, unsigned int vendor, int8_t tag);
-void           pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *vp);
-void           pairmemcpy(VALUE_PAIR *vp, uint8_t const * src, size_t len);
-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           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);
-VALUE_PAIR     *pairmake_ip(TALLOC_CTX *ctx, char const *value,
+int8_t         fr_pair_cmp_by_da_tag(void const *a, void const *b);
+void           fr_pair_list_sort(VALUE_PAIR **vps, fr_cmp_t cmp);
+void           fr_pair_validate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2]);
+bool           fr_pair_validate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list);
+bool           fr_pair_validate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list);
+VALUE_PAIR     *fr_pair_copy(TALLOC_CTX *ctx, VALUE_PAIR const *vp);
+VALUE_PAIR     *fr_pair_list_copy(TALLOC_CTX *ctx, VALUE_PAIR *from);
+VALUE_PAIR     *fr_pair_list_copy_by_num(TALLOC_CTX *ctx, VALUE_PAIR *from, unsigned int attr, unsigned int vendor, int8_t tag);
+void           fr_pair_steal(TALLOC_CTX *ctx, VALUE_PAIR *vp);
+void           fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const * src, size_t len);
+void           fr_pair_value_memsteal(VALUE_PAIR *vp, uint8_t const *src);
+void           fr_pair_value_strsteal(VALUE_PAIR *vp, char const *src);
+void           fr_pair_value_strcpy(VALUE_PAIR *vp, char const * src);
+void           fr_pair_value_bstrncpy(VALUE_PAIR *vp, void const * src, size_t len);
+void           fr_pair_value_sprintf(VALUE_PAIR *vp, char const * fmt, ...) CC_HINT(format (printf, 2, 3));
+void           fr_pair_list_move(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from);
+void           fr_pair_list_move_by_num(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from,
+                                        unsigned int attr, unsigned int vendor, int8_t tag);
+void           fr_pair_list_mcopy_by_num(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from,
+                                         unsigned int attr, unsigned int vendor, int8_t tag);
+VALUE_PAIR     *fr_pair_afrom_ip_str(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);
-int            readvp2(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone);
+int            fr_pair_value_from_str(VALUE_PAIR *vp, char const *value, size_t len);
+VALUE_PAIR     *fr_pair_make(TALLOC_CTX *ctx, VALUE_PAIR **vps, char const *attribute, char const *value, FR_TOKEN op);
+int            fr_pair_mark_xlat(VALUE_PAIR *vp, char const *value);
+FR_TOKEN       fr_pair_raw_from_str(char const **ptr, VALUE_PAIR_RAW *raw);
+FR_TOKEN       fr_pair_list_afrom_str(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **head);
+int            fr_pair_list_afrom_file(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone);
 
 
 /** Compare two attributes using and operator.
  *
  * @return 1 if equal, 0 if not eaqual, -1 on error.
  */
-#define                paircmp_op(_op, _a, _b) value_data_cmp_op(_op, _a->da->type, &_a->data, _a->vp_length, _b->da->type, &_b->data, _b->vp_length)
+#define                fr_pair_cmp_op(_op, _a, _b)     value_data_cmp_op(_op, _a->da->type, &_a->data, _a->vp_length, _b->da->type, &_b->data, _b->vp_length)
 
 /* value.c */
 int            value_data_cmp(PW_TYPE a_type, value_data_t const *a, size_t a_len,
@@ -673,6 +671,14 @@ ssize_t            value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
 ssize_t                value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE type,
                                const value_data_t *src, size_t src_len);
 
+size_t         value_data_prints(char *out, size_t outlen,
+                                 PW_TYPE type, DICT_ATTR const *enumv,
+                                 value_data_t const *data, ssize_t inlen, char quote);
+
+char           *value_data_aprints(TALLOC_CTX *ctx,
+                                   PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
+                                   size_t inlen, char quote);
+
 /*
  *     Error functions.
  */
@@ -684,7 +690,7 @@ char const  *fr_strerror(void);
 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_debug_lvl;   /* 0 = no debugging information */
 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];
@@ -702,7 +708,9 @@ 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, ssize_t inlen, bool resolve, bool fallback);
 int            fr_pton6(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback);
-int            fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve);
+int            fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve);
+int            fr_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af,
+                            bool resolve);
 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);
@@ -714,6 +722,7 @@ size_t              fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen);
 size_t         fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen);
 uint32_t       fr_strtoul(char const *value, char **end);
 bool           is_whitespace(char const *value);
+bool           is_printable(void const *value, size_t len);
 bool           is_integer(char const *value);
 bool           is_zero(char const *value);
 
@@ -728,6 +737,10 @@ 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);
+int            fr_nonblock(int fd);
+int            fr_blocking(int fd);
+ssize_t                fr_writev(int fd, struct iovec[], int iovcnt, struct timeval *timeout);
+
 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);
 int            fr_get_time(char const *date_str, time_t *date);
@@ -792,7 +805,6 @@ typedef enum {
 
 #define FR_FAULT_LOG(fmt, ...) fr_fault_log(fmt "\n", ## __VA_ARGS__)
 typedef void (*fr_fault_log_t)(char const *msg, ...) CC_HINT(format (printf, 1, 2));
-extern fr_fault_log_t fr_fault_log;
 extern fr_debug_state_t fr_debug_state;
 
 /** Optional callback passed to fr_fault_setup
@@ -820,14 +832,15 @@ int               fr_set_dumpable(bool allow_core_dumps);
 int            fr_reset_dumpable(void);
 int            fr_log_talloc_report(TALLOC_CTX *ctx);
 void           fr_fault(int sig);
+void           fr_talloc_fault_setup(void);
 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);
+void           fr_fault_log(char const *msg, ...) CC_HINT(format (printf, 1, 2));
 
 #  ifdef WITH_VERIFY_PTR
-void           fr_verify_vp(char const *file, int line, VALUE_PAIR const *vp);
-void           fr_verify_list(char const *file, int line, TALLOC_CTX *expected, VALUE_PAIR *vps);
+void           fr_pair_verify(char const *file, int line, VALUE_PAIR const *vp);
+void           fr_pair_list_verify(char const *file, int line, TALLOC_CTX *expected, VALUE_PAIR *vps);
 #  endif
 
 bool           fr_assert_cond(char const *file, int line, char const *expr, bool cond);
@@ -893,12 +906,20 @@ int               rbtree_walk(rbtree_t *tree, rb_order_t order, rb_walker_t compare, void *co
  */
 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);
+fr_fifo_t      *fr_fifo_create(TALLOC_CTX *ctx, 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);
+unsigned int   fr_fifo_num_elements(fr_fifo_t *fi);
+
+/*
+ *     socket.c
+ */
+int            fr_socket_client_unix(char const *path, bool async);
+int            fr_socket_client_udp(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, bool async);
+int            fr_socket_client_tcp(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, bool async);
+int            fr_socket_wait_for_connect(int sockfd, struct timeval *timeout);
 
 #ifdef __cplusplus
 }
diff --git a/src/include/listen.h b/src/include/listen.h
new file mode 100644 (file)
index 0000000..f7fd998
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *   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 LISTEN_H
+#define LISTEN_H
+/**
+ * $Id$
+ *
+ * @file listen.h
+ * @brief The listener API.
+ *
+ * @copyright 2015  The FreeRADIUS server project
+ */
+
+/*
+ *     Types of listeners.
+ *
+ *     Ordered by priority!
+ */
+typedef enum RAD_LISTEN_TYPE {
+       RAD_LISTEN_NONE = 0,
+       RAD_LISTEN_PROXY,
+       RAD_LISTEN_AUTH,
+       RAD_LISTEN_ACCT,
+       RAD_LISTEN_DETAIL,
+       RAD_LISTEN_VQP,
+       RAD_LISTEN_DHCP,
+       RAD_LISTEN_COMMAND,
+       RAD_LISTEN_COA,
+       RAD_LISTEN_MAX
+} RAD_LISTEN_TYPE;
+
+typedef enum RAD_LISTEN_STATUS {
+       RAD_LISTEN_STATUS_INIT = 0,
+       RAD_LISTEN_STATUS_KNOWN,
+       RAD_LISTEN_STATUS_FROZEN,
+       RAD_LISTEN_STATUS_EOL,
+       RAD_LISTEN_STATUS_REMOVE_NOW
+} RAD_LISTEN_STATUS;
+
+typedef struct rad_listen rad_listen_t;
+
+typedef int (*rad_listen_recv_t)(rad_listen_t *);
+typedef int (*rad_listen_send_t)(rad_listen_t *, REQUEST *);
+typedef int (*rad_listen_print_t)(rad_listen_t const *, char *, size_t);
+typedef int (*rad_listen_encode_t)(rad_listen_t *, REQUEST *);
+typedef int (*rad_listen_decode_t)(rad_listen_t *, REQUEST *);
+
+struct rad_listen {
+       rad_listen_t *next; /* should be rbtree stuff */
+
+       /*
+        *      For normal sockets.
+        */
+       RAD_LISTEN_TYPE type;
+       int             fd;
+       char const      *server;
+       int             status;
+#ifdef WITH_TCP
+       int             count;
+       bool            dual;
+       rbtree_t        *children;
+       rad_listen_t    *parent;
+#endif
+       bool            nodup;
+       bool            synchronous;
+       uint32_t        workers;
+
+#ifdef WITH_TLS
+       fr_tls_server_conf_t *tls;
+#endif
+
+       rad_listen_recv_t recv;
+       rad_listen_send_t send;
+       rad_listen_encode_t encode;
+       rad_listen_decode_t decode;
+       rad_listen_print_t print;
+
+       CONF_SECTION const *cs;
+       void            *data;
+
+#ifdef WITH_STATS
+       fr_stats_t      stats;
+#endif
+};
+
+/*
+ *     This shouldn't really be exposed...
+ */
+typedef struct listen_socket_t {
+       /*
+        *      For normal sockets.
+        */
+       fr_ipaddr_t     my_ipaddr;
+       uint16_t        my_port;
+
+       char const      *interface;
+#ifdef SO_BROADCAST
+       int             broadcast;
+#endif
+       time_t          rate_time;
+       uint32_t        rate_pps_old;
+       uint32_t        rate_pps_now;
+       uint32_t        max_rate;
+
+       /* for outgoing sockets */
+       home_server_t   *home;
+       fr_ipaddr_t     other_ipaddr;
+       uint16_t        other_port;
+
+       int             proto;
+
+#ifdef WITH_TCP
+       /* for a proxy connecting to home servers */
+       time_t          last_packet;
+       time_t          opened;
+       fr_event_t      *ev;
+
+       fr_socket_limit_t limit;
+
+       struct listen_socket_t *parent;
+       RADCLIENT       *client;
+
+       RADIUS_PACKET   *packet; /* for reading partial packets */
+#endif
+
+#ifdef WITH_TLS
+       tls_session_t   *ssn;
+       REQUEST         *request; /* horrible hacks */
+       VALUE_PAIR      *certs;
+       pthread_mutex_t mutex;
+       uint8_t         *data;
+       size_t          partial;
+#endif
+
+       RADCLIENT_LIST  *clients;
+} listen_socket_t;
+#endif /* LISTEN_H */
+
index c81faa7..2736591 100644 (file)
@@ -19,8 +19,9 @@
  * $Id$
  *
  * @file log.h
- * @brief Structures and prototypes for logging.
+ * @brief Macros and function definitions to write log messages, and control the logging system.
  *
+ * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
  * @copyright 2013 Alan DeKok <aland@freeradius.org>
  */
 RCSIDH(log_h, "$Id$")
@@ -44,8 +45,8 @@ typedef enum log_type {
        L_DBG_ERR_REQ = 20      //!< Less severe error only displayed when debugging is enabled.
 } log_type_t;
 
-typedef enum log_debug {
-       L_DBG_LVL_MIN = -1,     //!< Hack for stupid GCC warnings (comparison with 0 always true)
+typedef enum log_lvl {
+       L_DBG_LVL_DISABLE = -1, //!< Don't print messages.
        L_DBG_LVL_OFF = 0,      //!< No debug messages.
        L_DBG_LVL_1,            //!< Highest priority debug messages (-x).
        L_DBG_LVL_2,            //!< 2nd highest priority debug messages (-xx | -X).
@@ -73,7 +74,8 @@ typedef struct fr_log_t {
 
 typedef                void (*radlog_func_t)(log_type_t lvl, log_lvl_t priority, REQUEST *, char const *, va_list ap);
 
-extern FR_NAME_NUMBER const syslog_str2fac[];
+extern FR_NAME_NUMBER const syslog_facility_table[];
+extern FR_NAME_NUMBER const syslog_severity_table[];
 extern FR_NAME_NUMBER const log_str2dst[];
 extern fr_log_t default_log;
 
@@ -106,103 +108,228 @@ void    radlog_request_marker(log_type_t type, log_lvl_t lvl, REQUEST *request,
 
 void   fr_canonicalize_error(TALLOC_CTX *ctx, char **spaces, char **text, ssize_t slen, char const *msg);
 
-/*
- *     Logging macros.
- *
- *     For server code, do not call radlog, vradlog et al directly, use one of the logging macros instead.
- *
- *     R*                      - Macros prefixed with an R will automatically prepend request information to the
- *                               log messages.
- *     INFO | WARN | ERROR     - Macros containing these words will be displayed at all log levels.
- *     *DEBUG*                 - Macros with the word DEBUG, will only be displayed if the server or request debug
- *                               level is above 0.
- *     *[IWE]DEBUG[0-9]?       - Macros with I, W, E as (or just after) the prefix, will log with the priority
- *                               specified by the integer if the server or request log level at or above that integer.
- *                               If there is no integer the level is 1. The I|W|E prefix determines the type
- *                               (INFO, WARN, ERROR), if there is no I|W|E prefix the DEBUG type will be used.
- */
-
-/*
- *     Log server driven messages like threadpool exhaustion and connection failures
+/** @name Log global messages
+ *
+ * Write to the global log.
+ *
+ * Messages will always be written irrespective of the debugging level set with ``-x`` or ``-X``.
+ *
+ * @warning If a REQUEST * is **NOT** available, these macros **MUST** be used.
+ *
+ * @note These macros should only be used for important global events.
+ *
+ * **Debug categories**
+ * Name     | Syslog severity         | Colour/style | When to use
+ * -------- | ----------------------- | ------------ | -----------
+ * AUTH     | LOG_NOTICE              | Bold         | Never - Deprecated
+ * ACCT     | LOG_NOTICE              | Bold         | Never - Deprecated
+ * PROXY    | LOG_NOTICE              | Bold         | Never - Deprecated
+ * INFO     | LOG_INFO                | Bold         | TBD
+ * WARN     | LOG_WARNING             | Yellow       | Warnings. Impending resource exhaustion, resource exhaustion
+ * ERROR    | LOG_ERR                 | Red          | Critical server errors. Malformed queries, failed operations, connection errors, packet processing errors
+ *
+ * @{
  */
-#define _SL(_l, _p, _f, ...)   if (debug_flag >= _p) radlog(_l, _f, ## __VA_ARGS__)
+#define AUTH(fmt, ...)         radlog(L_AUTH, fmt, ## __VA_ARGS__)
+#define ACCT(fmt, ...)         radlog(L_ACCT, fmt, ## __VA_ARGS__)
+#define PROXY(fmt, ...)                radlog(L_PROXY, fmt, ## __VA_ARGS__)
 
-#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 INFO(fmt, ...)         radlog(L_INFO,  fmt, ## __VA_ARGS__)
+#define WARN(fmt, ...)         radlog(L_WARN, fmt, ## __VA_ARGS__)
+#define ERROR(fmt, ...)                radlog(L_ERR, fmt, ## __VA_ARGS__)
+/** @} */
 
-#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__)
-#define PROXY(fmt, ...)                _SL(L_PROXY, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+/** @name Log global debug messages (DEBUG*)
+ *
+ * Write debugging messages to the global log.
+ *
+ * Messages will be written if the debug level is high enough.
+ *
+ * **Debug categories**
+ * Name     | Syslog severity         | Colour/style | When to use
+ * -------- | ----------------------- | -------------| -----------
+ * DEBUG    | LOG_DEBUG               | Regular      | Normal debug output
+ *
+ * **Debug levels**
+ * Level    | Debug arguments         | Macro(s) enabled              | When to use
+ * -------- | ----------------------- | ----------------------------- | -----------
+ * 1        | ``-x``                  | DEBUG                         | Never - Deprecated
+ * 2        | ``-xx`` or ``-X``       | DEBUG, DEBUG2                 | Interactions with external entities. Connection management, control socket, triggers, etc...
+ * 3        | ``-xxx`` or ``-Xx``     | DEBUG, DEBUG2, DEBUG3         | Lower priority events. Polling for detail files, cleanups, etc...
+ * 4        | ``-xxxx`` or ``-Xxx``   | DEBUG, DEBUG2, DEBUG3, DEBUG4 | Internal server state debugging.
+ *
+ * @{
+ */
+#define DEBUG_ENABLED          debug_enabled(L_DBG, L_DBG_LVL_1)                       //!< True if global debug level 1 messages are enabled
+#define DEBUG_ENABLED2         debug_enabled(L_DBG, L_DBG_LVL_2)                       //!< True if global debug level 1-2 messages are enabled
+#define DEBUG_ENABLED3         debug_enabled(L_DBG, L_DBG_LVL_3)                       //!< True if global debug level 1-3 messages are enabled
+#define DEBUG_ENABLED4         debug_enabled(L_DBG, L_DBG_LVL_MAX)                     //!< True if global debug level 1-4 messages are enabled
 
+#define _SL(_l, _p, _f, ...)   if (rad_debug_lvl >= _p) radlog(_l, _f, ## __VA_ARGS__)
 #define DEBUG(fmt, ...)                _SL(L_DBG, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
 #define DEBUG2(fmt, ...)       _SL(L_DBG, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
 #define DEBUG3(fmt, ...)       _SL(L_DBG, L_DBG_LVL_3, fmt, ## __VA_ARGS__)
 #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 WARN(fmt, ...)         _SL(L_WARN, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define ERROR(fmt, ...)                _SL(L_ERR, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-
-/*
- *     Log request driven messages which including elements from the current request, like section and module
+/** @name Log request-specific messages (R*)
  *
- *     If a REQUEST * is available, these functions should be used.
+ * Write to the request log, or the global log if a request logging function is not set.
+ *
+ * Messages will always be written irrespective of the debugging level set with ``-x`` or ``-X``.
+ *
+ * @note Automatically prepends date (at lvl >= 3), request number, and module, to the log message.
+ * @note If a REQUEST * is available, these macros should be used.
+ * @note These macros should only be used for important global events.
+ *
+ * **Debug categories**
+ * Name     | Syslog severity         | Colour/style | When to use
+ * -------- | ----------------------- | -------------| -----------
+ * RAUTH    | LOG_NOTICE              | Bold         | Never - Deprecated
+ * RACCT    | LOG_NOTICE              | Bold         | Never - Deprecated
+ * RPROXY   | LOG_NOTICE              | Bold         | Never - Deprecated
+ * RINFO    | LOG_INFO                | Bold         | TBD
+ * RWARN    | LOG_WARNING             | Yellow/Bold  | Warnings. Impending resource exhaustion, or resource exhaustion.
+ * RERROR   | LOG_ERR                 | Red/Bold     | Critical server errors. Malformed queries, failed operations, connection errors, packet processing errors.
+ * @{
  */
-#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, ...)                radlog_request(L_AUTH, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
 #define RACCT(fmt, ...)                radlog_request(L_ACCT, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
 #define RPROXY(fmt, ...)       radlog_request(L_PROXY, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
+#define RINFO(fmt, ...)                radlog_request(L_INFO, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
+#define RWARN(fmt, ...)                radlog_request(L_DBG_WARN, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
+#define RERROR(fmt, ...)       radlog_request_error(L_DBG_ERR, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
+/** @} */
+
+/** @name Log request-specific debug (R*DEBUG*)
+ *
+ * Write debug messages to the request log.
+ *
+ * Messages will only be written if a request log function is set and the request or global
+ * debug level is high enough.
+ *
+ * @note Automatically prepends date (at lvl >= 3), request number, and module, to the log message.
+ *
+ * **Debug categories**
+ * Name     | Syslog severity         | Colour and style | When to use
+ * -------- | ----------------------- | -----------------| -----------
+ * RDEBUG*  | LOG_DEBUG               | Regular          | Normal debugging messages
+ * RIDEBUG* | LOG_DEBUG               | Bold             | Informational messages.
+ * RWDEBUG* | LOG_DEBUG               | Yellow/Bold      | Warnings. Invalid configuration, missing or invalid attributes etc...
+ * REDEBUG* | LOG_DEBUG               | Red/Bold         | Errors. Reject messages, bad values etc...
+ *
+ * **Debug levels**
+ * Level    | Debug arguments         | Macro(s) enabled                       | When to use
+ * -------- | ----------------------- | -------------------------------------- | -----------
+ * 1        | ``-x``                  | R*DEBUG                                | Never - Deprecated
+ * 2        | ``-xx`` or ``-X``       | R*DEBUG, R*DEBUG2                      | Normal request flow. Operations, Results of queries, or execs, etc...
+ * 3        | ``-xxx`` or ``-Xx``     | R*DEBUG, R*DEBUG2, R*DEBUG3            | Internal server state or packet input. State machine changes, extra attribute info, etc...
+ * 4        | ``-xxxx`` or ``-Xxx``   | R*DEBUG, R*DEBUG2, R*DEBUG3, R*DEBUG4  | Verbose internal server state messages or packet input. Hex dumps, structure dumps, pointer values.
+ *
+ * @{
+ */
+#define RDEBUG_ENABLED         radlog_debug_enabled(L_DBG, L_DBG_LVL_1, request)       //!< True if request debug level 1 messages are enabled
+#define RDEBUG_ENABLED2                radlog_debug_enabled(L_DBG, L_DBG_LVL_2, request)       //!< True if request debug level 1-2 messages are enabled
+#define RDEBUG_ENABLED3                radlog_debug_enabled(L_DBG, L_DBG_LVL_3, request)       //!< True if request debug level 1-3 messages are enabled
+#define RDEBUG_ENABLED4                radlog_debug_enabled(L_DBG, L_DBG_LVL_MAX, request)     //!< True if request debug level 1-4 messages are enabled
 
 #define RDEBUGX(_l, fmt, ...)  radlog_request(L_DBG, _l, request, fmt, ## __VA_ARGS__)
-#define RDEBUG(fmt, ...)       if (debug_flag || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_1, request, fmt, ## __VA_ARGS__)
-#define RDEBUG2(fmt, ...)      if (debug_flag || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_2, request, fmt, ## __VA_ARGS__)
-#define RDEBUG3(fmt, ...)      if (debug_flag || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_3, request, fmt, ## __VA_ARGS__)
-#define RDEBUG4(fmt, ...)      if (debug_flag || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_MAX, request, fmt, ## __VA_ARGS__)
+#define RDEBUG(fmt, ...)       if (rad_debug_lvl || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_1, request, fmt, ## __VA_ARGS__)
+#define RDEBUG2(fmt, ...)      if (rad_debug_lvl || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_2, request, fmt, ## __VA_ARGS__)
+#define RDEBUG3(fmt, ...)      if (rad_debug_lvl || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_3, request, fmt, ## __VA_ARGS__)
+#define RDEBUG4(fmt, ...)      if (rad_debug_lvl || request->log.lvl) radlog_request(L_DBG, L_DBG_LVL_MAX, request, fmt, ## __VA_ARGS__)
 
-#define RINFO(fmt, ...)                radlog_request(L_INFO, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
 #define RIDEBUG(fmt, ...)      radlog_request(L_INFO, L_DBG_LVL_1, request, fmt, ## __VA_ARGS__)
 #define RIDEBUG2(fmt, ...)     radlog_request(L_INFO, L_DBG_LVL_2, request, fmt, ## __VA_ARGS__)
 
-#define RWARN(fmt, ...)                radlog_request(L_DBG_WARN, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
-#define RWDEBUG(fmt, ...)      if (debug_flag || request->log.lvl) radlog_request(L_DBG_WARN, L_DBG_LVL_1, request, fmt, ## __VA_ARGS__)
-#define RWDEBUG2(fmt, ...)     if (debug_flag || request->log.lvl) radlog_request(L_DBG_WARN, L_DBG_LVL_2, request, fmt, ## __VA_ARGS__)
+#define RWDEBUG(fmt, ...)      if (rad_debug_lvl || request->log.lvl) radlog_request(L_DBG_WARN, L_DBG_LVL_1, request, fmt, ## __VA_ARGS__)
+#define RWDEBUG2(fmt, ...)     if (rad_debug_lvl || request->log.lvl) radlog_request(L_DBG_WARN, L_DBG_LVL_2, request, fmt, ## __VA_ARGS__)
 
-#define RERROR(fmt, ...)       radlog_request_error(L_DBG_ERR, L_DBG_LVL_OFF, request, fmt, ## __VA_ARGS__)
 #define REDEBUG(fmt, ...)      radlog_request_error(L_DBG_ERR, L_DBG_LVL_1, request, fmt, ## __VA_ARGS__)
 #define REDEBUG2(fmt, ...)     radlog_request_error(L_DBG_ERR, L_DBG_LVL_2, request, fmt, ## __VA_ARGS__)
 #define REDEBUG3(fmt, ...)     radlog_request_error(L_DBG_ERR, L_DBG_LVL_3, request, fmt, ## __VA_ARGS__)
 #define REDEBUG4(fmt, ...)     radlog_request_error(L_DBG_ERR, L_DBG_LVL_MAX, request, fmt, ## __VA_ARGS__)
+/** @} */
 
-#define RINDENT()              (request->log.indent += 2)
-#define REXDENT()              (request->log.indent -= 2)
+/** Indent R* messages by one level
+ *
+ * @note Has no effect on the indentation of INFO, WARN, ERROR, DEBUG messages,
+ *      only RINFO, RWARN, RERROR etc...
+ */
+#define RINDENT() (request->log.indent += 2)
 
-/*
- * Output string with error marker, showing where format error occurred.
+/** Exdent (unindent) R* messages by one level
+ *
+ * @note Has no effect on the indentation of INFO, WARN, ERROR, DEBUG messages,
+ *      only RINFO, RWARN, RERROR etc...
+ */
+#define REXDENT() (request->log.indent -= 2)
+
+/** Output string with error marker, showing where format error occurred
  *
  @verbatim
    my pet kitty
       ^ kitties are not pets, are nature devouring hell beasts
  @endverbatim
  *
+ * @warning If a REQUEST * is **NOT** available, or is NULL, this macro must **NOT** be used.
+ *
+ * @param _l log category, a log_type_t value.
+ * @param _p log priority, a log_lvl_t value.
  * @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 _RMKR(_l, _p, _m, _i, _e)      radlog_request_marker(_l, _p, request, _m, _i, _e)
-#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 RMARKER(_l, _p, _m, _i, _e)    radlog_request_marker(_l, _p, request, _m, _i, _e)
 
-/*
- *     Use different logging functions depending on whether
- *     request is NULL or not.
+/** Output string with error marker, showing where format error occurred
+ *
+ * These are logged as RERROR messages.
+ *
+ @verbatim
+   my pet kitty
+      ^ kitties are not pets, are nature devouring hell beasts
+ @endverbatim
+ *
+ * @warning If a REQUEST * is **NOT** available, or is NULL, this macro must **NOT** be used.
+ *
+ * @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 ROPTIONAL(_l_request, _l_global, fmt, ...) \
+#define REMARKER(_m, _i, _e)           RMARKER(L_DBG_ERR, L_DBG_LVL_1, _m, _i, _e)
+
+/** Output string with error marker, showing where format error occurred
+ *
+ * These are logged as RDEBUG messages.
+ *
+ @verbatim
+   my pet kitty
+      ^ kitties are not pets, are nature devouring hell beasts
+ @endverbatim
+ *
+ * @warning If a REQUEST * is **NOT** available, or is NULL, this macro must **NOT** be used.
+ *
+ * @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 RDMARKER(_m, _i, _e)           RMARKER(L_DBG, L_DBG_LVL_1, _m, _i, _e)
+
+/** Use different logging functions depending on whether request is NULL or not.
+ *
+ * @note The module must define MOD_PREFIX as its name (do this in the module
+ *      header file) e.g. @code{.c}#define MOD_PREFIX "rlm_example"@endcode
+ *
+ * This is useful for areas of code which are run on server startup, and when
+ * processing requests.
+ *
+ * @param _l_request The name of a R* logging macro e.g. RDEBUG3.
+ * @param _l_global The name of a global logging macro e.g. DEBUG3.
+ * @param fmt printf style format string.
+ * @param ... printf arguments.
+ */
+ #define MOD_ROPTIONAL(_l_request, _l_global, fmt, ...) \
 do {\
        if (request) {\
                _l_request(fmt, ## __VA_ARGS__);\
@@ -211,10 +338,39 @@ do {\
        }\
 } while (0)
 
-/*
- *     Rate limit messages.
+/** Use different logging functions depending on whether request is NULL or not.
+ *
+ * This is useful for areas of code which are run on server startup, and when
+ * processing requests.
+ *
+ * @param _l_request The name of a R* logging macro e.g. RDEBUG3.
+ * @param _l_global The name of a global logging macro e.g. DEBUG3.
+ * @param fmt printf style format string.
+ * @param ... printf arguments.
+ */
+ #define ROPTIONAL(_l_request, _l_global, fmt, ...) \
+do {\
+       if (request) {\
+               _l_request(fmt, ## __VA_ARGS__);\
+       } else {\
+               _l_global(LOG_PREFIX ": " fmt, ## __VA_ARGS__);\
+       }\
+} while (0)
+
+#define RATE_LIMIT_ENABLED rate_limit_enabled()                //!< True if rate limiting is enabled.
+/** Rate limit messages
+ *
+ * Rate limit log messages so they're written a maximum of once per second.
+ *
+ @code{.c}
+   RATE_LIMIT(RERROR("Home servers alive in pool %s", pool->name));
+ @endcode
+ * @note Rate limits the macro, not the message. If five different messages are
+ *      produced using the same macro in the same second, only the first will
+ *      be written to the log.
+ *
+ * @param _x Logging macro to limit.
  */
-#define RATE_LIMIT_ENABLED rate_limit_enabled()
 #define RATE_LIMIT(_x) \
 do {\
        if (RATE_LIMIT_ENABLED) {\
index 2ca6379..584c76b 100644 (file)
@@ -21,7 +21,8 @@
  * @file map.h
  * @brief Structures and prototypes for maps
  *
- * @copyright 2013  The FreeRADIUS server project
+ * @copyright 2015 The FreeRADIUS server project
+ * @copyright 2015 Arran Cudbard-bell <a.cudbardb@freeradius.org>
  */
 
 RCSIDH(map_h, "$Id$")
@@ -41,23 +42,19 @@ extern "C" {
  * Neither src or dst need to be an FR attribute, and their type can be inferred
  * from whether map->da is NULL (not FR).
  *
- * @see value_pair_tmpl_t
+ * @see vp_tmpl_t
  */
-typedef struct value_pair_map {
-       value_pair_tmpl_t       *lhs;   //!< Typically describes the attribute
-                                       //!< to add or modify.
-       value_pair_tmpl_t       *rhs;   //!< Typically describes a value or a
-                                       //!< src attribute to copy.
+typedef struct vp_map {
+       vp_tmpl_t               *lhs;   //!< Typically describes the attribute to add, modify or compare.
+       vp_tmpl_t               *rhs;   //!< Typically describes a literal value or a src attribute to copy or compare.
 
-       FR_TOKEN                op;     //!< The operator that controls
-                                       //!< insertion of the dst attribute.
+       FR_TOKEN                op;     //!< The operator that controls insertion of the dst attribute.
 
-       CONF_ITEM               *ci;    //!< Config item that the map was
-                                       //!< created from. Mainly used for
+       CONF_ITEM               *ci;    //!< Config item that the map was created from. Mainly used for
                                        //!< logging validation errors.
 
-       struct value_pair_map   *next;  //!< The next valuepair map.
-} value_pair_map_t;
+       struct vp_map           *next;  //!< The next valuepair map.
+} vp_map_t;
 
 #ifndef WITH_VERIFY_PTR
 #  define VERIFY_MAP(_x) rad_assert((_x)->lhs)
@@ -68,40 +65,45 @@ typedef struct value_pair_map {
 } while (0)
 #endif
 
-typedef int (*map_validate_t)(value_pair_map_t *map, void *ctx);
-typedef int (*radius_map_getvalue_t)(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, void *ctx);
+typedef int (*map_validate_t)(vp_map_t *map, void *ctx);
+typedef int (*radius_map_getvalue_t)(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request,
+                                    vp_map_t const *map, void *uctx);
 
-int            map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
+int            map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
                             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            map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, FR_TOKEN lhs_type,
+int            map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out, char const *lhs, FR_TOKEN lhs_type,
                                 FR_TOKEN op, char const *rhs, FR_TOKEN rhs_type,
                                 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            map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs,
+int            map_afrom_cs(vp_map_t **out, CONF_SECTION *cs,
                             pair_lists_t dst_list_def, pair_lists_t src_list_def,
                             map_validate_t validate, void *ctx, unsigned int max) CC_HINT(nonnull(1, 2));
 
-int            map_afrom_attr_str(TALLOC_CTX *ctx, value_pair_map_t **out, 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            map_afrom_attr_str(TALLOC_CTX *ctx, vp_map_t **out, 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);
+
+int8_t         map_cmp_by_lhs_attr(void const *a, void const *b);
+
+void           map_sort(vp_map_t **maps, fr_cmp_t cmp);
 
-int            map_to_vp(VALUE_PAIR **out, REQUEST *request,
-                         value_pair_map_t const *map, void *ctx) CC_HINT(nonnull (1,2,3));
+int            map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request,
+                         vp_map_t const *map, void *uctx) CC_HINT(nonnull (2,3,4));
 
-int            map_to_request(REQUEST *request, value_pair_map_t const *map,
+int            map_to_request(REQUEST *request, vp_map_t const *map,
                               radius_map_getvalue_t func, void *ctx);
 
-bool           map_dst_valid(REQUEST *request, value_pair_map_t const *map);
+bool           map_dst_valid(REQUEST *request, vp_map_t const *map);
 
-size_t         map_prints(char *buffer, size_t bufsize, value_pair_map_t const *map);
+size_t         map_prints(char *buffer, size_t bufsize, vp_map_t const *map);
 
-void           map_debug_log(REQUEST *request, value_pair_map_t const *map,
+void           map_debug_log(REQUEST *request, vp_map_t const *map,
                              VALUE_PAIR const *vp) CC_HINT(nonnull(1, 2));
 
-bool map_cast_from_hex(value_pair_map_t *map, FR_TOKEN rhs_type, char const *rhs);
+bool           map_cast_from_hex(vp_map_t *map, FR_TOKEN rhs_type, char const *rhs);
 #ifdef __cplusplus
 }
 #endif
index cf9b26f..b7bdd6a 100644 (file)
@@ -1,9 +1,10 @@
-/*
- * md4.h       Structures and prototypes for md4.
+/**
+ * $Id$
  *
- * Version:     $Id$
- * License:            LGPL, but largely derived from a public domain source.
+ * @note license is LGPL, but largely derived from a public domain source.
  *
+ * @file md4.h
+ * @brief Structures and prototypes for md4.
  */
 
 #ifndef _FR_MD4_H
 RCSIDH(md4_h, "$Id$")
 
 #ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
+#  include <inttypes.h>
 #endif
 
 #ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
+#  include <sys/types.h>
 #endif
 
 #ifdef HAVE_STDINT_H
-#include <stdint.h>
+#  include <stdint.h>
 #endif
 
 #include <string.h>
 
 #ifdef HAVE_OPENSSL_MD4_H
-#include <openssl/md4.h>
+#  include <openssl/md4.h>
 #endif
 
 #ifdef __cplusplus
@@ -37,21 +38,11 @@ extern "C" {
 #  define MD4_DIGEST_LENGTH 16
 #endif
 
-void fr_md4_calc (unsigned char *, unsigned char const *, unsigned int);
-
 #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]
- *  Commented out #include <sys/cdefs.h>
- *  Commented out the __BEGIN and __END _DECLS, and the __attributes.
- *  Commented out MD4End, MD4File, MD4Data
- *  Commented out header file protection #ifndef,#define,#endif
- */
-
-/*     $OpenBSD: md4.h,v 1.12 2004/04/28 16:54:00 millert Exp $        */
-
 /*
+ * The MD5 code used here and in md4.c was originally retrieved from:
+ *   http://www.openbsd.org/cgi-bin/cvsweb/src/include/md4.h?rev=1.12
+ *
  * This code implements the MD4 message-digest algorithm.
  * The algorithm is due to Ron Rivest.  This code was
  * written by Colin Plumb in 1993, no copyright is claimed.
@@ -63,40 +54,36 @@ void fr_md4_calc (unsigned char *, unsigned char const *, unsigned int);
  * except that you don't need to include two pages of legalese
  * with every copy.
  */
-
-/*#ifndef _MD4_H_*/
-/*#define _MD4_H_*/
-
-#define        MD4_BLOCK_LENGTH                64
-#define        MD4_DIGEST_STRING_LENGTH        (MD4_DIGEST_LENGTH * 2 + 1)
+#  define MD4_BLOCK_LENGTH 64
+#  define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1)
 
 typedef struct FR_MD4Context {
-       uint32_t state[4];                      /* state */
-       uint32_t count[2];                      /* number of bits, mod 2^64 */
-       uint8_t buffer[MD4_BLOCK_LENGTH];       /* input buffer */
+       uint32_t state[4];                      //!< State.
+       uint32_t count[2];                      //!< Number of bits, mod 2^64.
+       uint8_t buffer[MD4_BLOCK_LENGTH];       //!< Input buffer.
 } FR_MD4_CTX;
 
-/*__BEGIN_DECLS*/
-void    fr_md4_init(FR_MD4_CTX *);
-void    fr_md4_update(FR_MD4_CTX *, uint8_t const *, size_t)
-/*             __attribute__((__bounded__(__string__,2,3)))*/;
-void    fr_md4_final(uint8_t [MD4_DIGEST_LENGTH], FR_MD4_CTX *)
-/*             __attribute__((__bounded__(__minbytes__,1,MD4_DIGEST_LENGTH)))*/;
-void    fr_md4_transform(uint32_t [4], uint8_t const [MD4_BLOCK_LENGTH])
-/*             __attribute__((__bounded__(__minbytes__,1,4)))
-               __attribute__((__bounded__(__minbytes__,2,MD4_BLOCK_LENGTH)))*/;
-/*__END_DECLS*/
+void   fr_md4_init(FR_MD4_CTX *ctx);
+void   fr_md4_update(FR_MD4_CTX *ctx, uint8_t const *in, size_t inlen)
+       CC_BOUNDED(__string__, 2, 3);
+void   fr_md4_final(uint8_t out[MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
+       CC_BOUNDED(__minbytes__, 1, MD4_DIGEST_LENGTH);
+void   fr_md4_transform(uint32_t buf[4], uint8_t const inc[MD4_BLOCK_LENGTH])
+       CC_BOUNDED(__size__, 1, 4, 4)
+       CC_BOUNDED(__minbytes__, 2, MD4_BLOCK_LENGTH);
 #else  /* HAVE_OPENSSL_MD4_H */
 USES_APPLE_DEPRECATED_API
-#define FR_MD4_CTX     MD4_CTX
-#define fr_md4_init    MD4_Init
-#define fr_md4_update  MD4_Update
-#define fr_md4_final   MD4_Final
-#define fr_md4_transform MD4_Transform
+#  define FR_MD4_CTX           MD4_CTX
+#  define fr_md4_init          MD4_Init
+#  define fr_md4_update                MD4_Update
+#  define fr_md4_final         MD4_Final
+#  define fr_md4_transform     MD4_Transform
 #endif
 
+/* md4.c */
+void fr_md4_calc(uint8_t out[MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen);
+
 #ifdef __cplusplus
 }
 #endif
-
 #endif /* _FR_MD4_H */
index 057b927..a445845 100644 (file)
 RCSIDH(md5_h, "$Id$")
 
 #ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
+#  include <inttypes.h>
 #endif
 
 #ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
+#  include <sys/types.h>
 #endif
 
 #ifdef HAVE_STDINT_H
-#include <stdint.h>
+#  include <stdint.h>
 #endif
 
-#include <string.h>
+#  include <string.h>
 
 #ifdef HAVE_OPENSSL_MD5_H
-#include <openssl/md5.h>
+#  include <openssl/md5.h>
 #endif
 
 #ifdef __cplusplus
@@ -39,14 +39,10 @@ extern "C" {
 #endif
 
 #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]
- *  Commented out #include <sys/cdefs.h>
- *  Commented out the __BEGIN and __END _DECLS, and the __attributes.
- */
-
 /*
+ * The MD5 code used here and in md5.c was originally retrieved from:
+ *   http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/crypto/md5.h?rev=1.1
+ *
  * This code implements the MD5 message-digest algorithm.
  * The algorithm is due to Ron Rivest.  This code was
  * written by Colin Plumb in 1993, no copyright is claimed.
@@ -57,42 +53,37 @@ extern "C" {
  * except that you don't need to include two pages of legalese
  * with every copy.
  */
-
-#define        MD5_BLOCK_LENGTH                64
-
+#  define MD5_BLOCK_LENGTH 64
 typedef struct FR_MD5Context {
-       uint32_t state[4];                      /* state */
-       uint32_t count[2];                      /* number of bits, mod 2^64 */
-       uint8_t buffer[MD5_BLOCK_LENGTH];       /* input buffer */
+       uint32_t state[4];                      //!< State.
+       uint32_t count[2];                      //!< Number of bits, mod 2^64.
+       uint8_t buffer[MD5_BLOCK_LENGTH];       //!< Input buffer.
 } FR_MD5_CTX;
 
-/* include <sys/cdefs.h> */
-
-/* __BEGIN_DECLS */
-void    fr_md5_init(FR_MD5_CTX *);
-void    fr_md5_update(FR_MD5_CTX *, uint8_t const *, size_t)
-/*             __attribute__((__bounded__(__string__,2,3)))*/;
-void    fr_md5_final(uint8_t [MD5_DIGEST_LENGTH], FR_MD5_CTX *)
-/*             __attribute__((__bounded__(__minbytes__,1,MD5_DIGEST_LENGTH)))*/;
-void    fr_md5_transform(uint32_t [4], uint8_t const [MD5_BLOCK_LENGTH])
-/*             __attribute__((__bounded__(__minbytes__,1,4)))*/
-/*             __attribute__((__bounded__(__minbytes__,2,MD5_BLOCK_LENGTH)))*/;
-/* __END_DECLS */
-
+void   fr_md5_init(FR_MD5_CTX *ctx);
+void   fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen)
+       CC_BOUNDED(__string__, 2, 3);
+void   fr_md5_final(uint8_t out[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx)
+       CC_BOUNDED(__minbytes__, 1, MD5_DIGEST_LENGTH);
+void   fr_md5_transform(uint32_t state[4], uint8_t const block[MD5_BLOCK_LENGTH])
+       CC_BOUNDED(__size__, 1, 4, 4)
+       CC_BOUNDED(__minbytes__, 2, MD5_BLOCK_LENGTH);
 #else  /* HAVE_OPENSSL_MD5_H */
-
 USES_APPLE_DEPRECATED_API
-#define FR_MD5_CTX     MD5_CTX
-#define fr_md5_init    MD5_Init
-#define fr_md5_update  MD5_Update
-#define fr_md5_final   MD5_Final
-#define fr_md5_transform MD5_Transform
+#  define FR_MD5_CTX           MD5_CTX
+#  define fr_md5_init          MD5_Init
+#  define fr_md5_update                MD5_Update
+#  define fr_md5_final         MD5_Final
+#  define fr_md5_transform     MD5_Transform
 #endif
 
 /* hmac.c */
+void   fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *text, size_t text_len,
+                   uint8_t const *key, size_t key_len)
+       CC_BOUNDED(__minbytes__, 1, MD5_DIGEST_LENGTH);
 
-void fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *text, size_t text_len,
-                uint8_t const *key, size_t key_len);
+/* md5.c */
+void   fr_md5_calc(uint8_t *out, uint8_t const *in, size_t inlen);
 
 #ifdef __cplusplus
 }
index 32012c4..b0960f6 100644 (file)
@@ -356,7 +356,7 @@ int gettimeofday (struct timeval *tv, void *tz);
 #endif
 
 /*
- *     Work around diffrent ctime_r styles
+ *     Work around different ctime_r styles
  */
 #if defined(CTIMERSTYLE) && (CTIMERSTYLE == SOLARISSTYLE)
 #  define CTIME_R(a,b,c) ctime_r(a,b,c)
@@ -403,7 +403,7 @@ typedef struct int128_t { uint8_t v[16]; } int128_t;
 
 /* abcd efgh -> dcba hgfe -> hgfe dcba */
 #ifndef HAVE_HTONLL
-#  ifdef RADIUS_LITTLE_ENDIAN
+#  ifdef FR_LITTLE_ENDIAN
 #    ifdef HAVE_BUILTIN_BSWAP64
 #      define ntohll(x) __builtin_bswap64(x)
 #    else
@@ -416,7 +416,7 @@ typedef struct int128_t { uint8_t v[16]; } int128_t;
 #endif
 
 #ifndef HAVE_HTONLLL
-#  ifdef RADIUS_LITTLE_ENDIAN
+#  ifdef FR_LITTLE_ENDIAN
 #    ifdef HAVE_128BIT_INTEGERS
 #      define ntohlll(x) (((uint128_t)ntohll((uint64_t)(x >> 64))) | (((uint128_t)ntohll(((uint64_t) x)) << 64)))
 #    else
@@ -436,6 +436,11 @@ typedef void(*sig_t)(int);
 }
 #endif
 
+/*
+ *     Not really missing, but may be submitted as patches
+ *     to the talloc project at some point in the future.
+ */
 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));
+char *talloc_bstrndup(const void *t, char const *in, size_t inlen);
 #endif /* _FR_MISSING_H */
index 3ca6442..ac92777 100644 (file)
@@ -21,7 +21,7 @@ extern "C" {
  */
 typedef struct modcallable modcallable;
 
-int modcall_fixup_update(value_pair_map_t *map, void *ctx);
+int modcall_fixup_update(vp_map_t *map, void *ctx);
 
 int modcall(rlm_components_t component, modcallable *c, REQUEST *request);
 
index 94d320e..d5e2c23 100644 (file)
@@ -49,12 +49,16 @@ typedef struct module_instance_t {
        pthread_mutex_t         *mutex;
 #endif
        CONF_SECTION            *cs;
+       time_t                  last_hup;
+       bool                    instantiated;
        bool                    force;
        rlm_rcode_t             code;
        fr_module_hup_t         *mh;
 } module_instance_t;
 
-module_instance_t      *find_module_instance(CONF_SECTION *modules, char const *askedname, bool do_link);
+module_instance_t      *module_instantiate(CONF_SECTION *modules, char const *askedname);
+module_instance_t      *module_instantiate_method(CONF_SECTION *modules, char const *askedname, rlm_components_t *method);
+module_instance_t      *module_find(CONF_SECTION *modules, char const *askedname);
 int                    find_module_sibling_section(CONF_SECTION **out, CONF_SECTION *module, char const *name);
 int                    module_hup_module(CONF_SECTION *cs, module_instance_t *node, time_t when);
 
index 68bcea0..9ba81b3 100644 (file)
@@ -40,19 +40,19 @@ extern "C" {
  * Used as indexes in the methods array in the module_t struct.
  */
 typedef enum rlm_components {
-       RLM_COMPONENT_AUTH = 0, //!< 0 methods index for authenticate section.
-       RLM_COMPONENT_AUTZ,     //!< 1 methods index for authorize section.
-       RLM_COMPONENT_PREACCT,  //!< 2 methods index for preacct section.
-       RLM_COMPONENT_ACCT,     //!< 3 methods index for accounting section.
-       RLM_COMPONENT_SESS,     //!< 4 methods index for checksimul section.
-       RLM_COMPONENT_PRE_PROXY,//!< 5 methods index for preproxy section.
-       RLM_COMPONENT_POST_PROXY, //!< 6 methods index for postproxy section.
-       RLM_COMPONENT_POST_AUTH,//!< 7 methods index for postauth section.
+       MOD_AUTHENTICATE = 0,   //!< 0 methods index for authenticate section.
+       MOD_AUTHORIZE,          //!< 1 methods index for authorize section.
+       MOD_PREACCT,            //!< 2 methods index for preacct section.
+       MOD_ACCOUNTING,         //!< 3 methods index for accounting section.
+       MOD_SESSION,            //!< 4 methods index for checksimul section.
+       MOD_PRE_PROXY,          //!< 5 methods index for preproxy section.
+       MOD_POST_PROXY,         //!< 6 methods index for postproxy section.
+       MOD_POST_AUTH,          //!< 7 methods index for postauth section.
 #ifdef WITH_COA
-       RLM_COMPONENT_RECV_COA, //!< 8 methods index for recvcoa section.
-       RLM_COMPONENT_SEND_COA, //!< 9 methods index for sendcoa section.
+       MOD_RECV_COA,           //!< 8 methods index for recvcoa section.
+       MOD_SEND_COA,           //!< 9 methods index for sendcoa section.
 #endif
-       RLM_COMPONENT_COUNT     //!< 10 how many components there are.
+       MOD_COUNT               //!< 10 how many components there are.
 } rlm_components_t;
 
 extern const FR_NAME_NUMBER mod_rcode_table[];
@@ -78,11 +78,6 @@ 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_UNSAFE (1 << 1)  //!< Don't instantiate module on -C.
-                                               //!< Module will NOT be
-                                               //!< instantiated if the server
-                                               //!< is started in config
-                                               //!< check mode.
 #define RLM_TYPE_HUP_SAFE      (1 << 2)        //!< Will be restarted on HUP.
                                                //!< Server will instantiated
                                                //!< new instance, and then
@@ -135,17 +130,15 @@ typedef int (*detach_t)(void *instance);
  * within the module to different sections.
  */
 typedef struct module_t {
-       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
-       CONF_PARSER const       *config;                        //!< Configuration information
-       instantiate_t           instantiate;                    //!< Function to use for instantiation.
-       detach_t                detach;                         //!< Function to use to free module instance.
-       packetmethod            methods[RLM_COMPONENT_COUNT];   //!< Pointers to the various section functions, ordering
-                                                               //!< determines which function is mapped to
-                                                               //!< which section.
-
+       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
+       CONF_PARSER const       *config;                //!< Configuration information
+       instantiate_t           bootstrap;              //!< register dynamic attrs, etc.
+       instantiate_t           instantiate;            //!< Function to use for instantiation.
+       detach_t                detach;                 //!< Function to use to free module instance.
+       packetmethod            methods[MOD_COUNT];     //!< Pointers to the various section functions.
 } module_t;
 
 int modules_init(CONF_SECTION *);
index d349f54..6184233 100644 (file)
@@ -2,9 +2,8 @@
 #define FR_NET_H
 /*
  *   This program is 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.
+ *   it under the terms of the GNU General Public License, version 2 of 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
@@ -83,7 +82,7 @@ typedef enum {
 /*
  *     Structure of a DEC/Intel/Xerox or 802.3 Ethernet header.
  */
-typedef struct ethernet_header {
+typedef struct CC_HINT(__packed__) ethernet_header {
        uint8_t         ether_dst[ETHER_ADDR_LEN];
        uint8_t         ether_src[ETHER_ADDR_LEN];
        uint16_t        ether_type;
@@ -92,7 +91,7 @@ typedef struct ethernet_header {
 /*
  *     Structure of an internet header, naked of options.
  */
-typedef struct ip_header {
+typedef struct CC_HINT(__packed__) ip_header {
        uint8_t         ip_vhl;         //!< Header length, version.
 
        uint8_t         ip_tos;         //!< Type of service.
@@ -106,7 +105,7 @@ typedef struct ip_header {
        struct in_addr  ip_src, ip_dst; //!< Src and Dst address
 } ip_header_t;
 
-typedef struct ip_header6 {
+typedef struct CC_HINT(__packed__) ip_header6 {
        uint32_t        ip_vtcfl;       //!< Version, traffic class, flow label.
        uint16_t        ip_len;         //!< Payload length
 
@@ -120,14 +119,14 @@ typedef struct ip_header6 {
  *     UDP protocol header.
  *     Per RFC 768, September, 1981.
  */
-typedef struct udp_header {
+typedef struct CC_HINT(__packed__) 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 {
+typedef struct CC_HINT(__packed__) radius_packet_t {
        uint8_t         code;
        uint8_t         id;
        uint8_t         length[2];
@@ -135,8 +134,9 @@ typedef struct radius_packet_t {
        uint8_t         data[];
 } radius_packet_t;
 
-ssize_t fr_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);
-uint16_t fr_iph_checksum(uint8_t const *data, uint8_t ihl);
+bool           fr_link_layer_supported(int link_layer);
+ssize_t                fr_link_layer_offset(uint8_t const *data, size_t len, int link_layer);
+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);
+uint16_t       fr_iph_checksum(uint8_t const *data, uint8_t ihl);
 #endif /* FR_NET_H */
index 1e31c12..7e41a8d 100644 (file)
@@ -35,7 +35,6 @@ 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, uint16_t port);
-int fr_nonblock(int fd);
 
 typedef struct fr_packet_list_t fr_packet_list_t;
 
index c539e28..9f3fdeb 100644 (file)
@@ -74,8 +74,8 @@ struct fr_cond_t {
 
        CONF_ITEM const *ci;
        union {
-               value_pair_map_t *map;
-               value_pair_tmpl_t *vpt;
+               vp_map_t *map;
+               vp_tmpl_t *vpt;
                fr_cond_t       *child;
        } data;
 
index f25a087..6444943 100644 (file)
@@ -3,9 +3,8 @@
 #ifdef HAVE_LIBPCAP
 /*
  *   This program is 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.
+ *   it under the terms of the GNU General Public License, version 2 of 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
@@ -82,7 +81,7 @@ struct fr_pcap {
        pcap_t                  *handle;                        //!< libpcap handle.
        pcap_dumper_t           *dumper;                        //!< libpcap dumper handle.
 
-       int                     link_type;                      //!< Link layer type.
+       int                     link_layer;                     //!< 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.
@@ -90,9 +89,10 @@ struct fr_pcap {
        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);
+int            fr_pcap_if_link_layer(char *errbuff, pcap_if_t *dev);
+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);
 #endif
 #endif
index ad447d9..d39ddba 100644 (file)
 
 RCSIDH(process_h, "$Id$")
 
+#include <freeradius-devel/clients.h>
+#include <freeradius-devel/listen.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef enum fr_saction_t {    /* server action */
+typedef enum fr_state_action_t {       /* server action */
        FR_ACTION_INVALID = 0,
        FR_ACTION_RUN,
        FR_ACTION_DONE,
        FR_ACTION_DUP,
-       FR_ACTION_CONFLICTING,
        FR_ACTION_TIMER,
 #ifdef WITH_PROXY
        FR_ACTION_PROXY_REPLY,
 #endif
-} fr_saction_t;
+} fr_state_action_t;
+
+/*
+ *  Function handler for requests.
+ */
+typedef        int (*RAD_REQUEST_FUNP)(REQUEST *);
+typedef        void (*fr_request_process_t)(REQUEST *, int);
 
 extern time_t fr_start_time;
 
index a9001c1..1eb10f3 100644 (file)
@@ -33,19 +33,19 @@ extern "C" {
  *     Logging macros
  */
  #undef DEBUG
-#define DEBUG(fmt, ...)                if (do_output && (fr_debug_flag > 0)) fprintf(fr_log_fp, fmt "\n", ## __VA_ARGS__)
+#define DEBUG(fmt, ...)                if (do_output && (fr_debug_lvl > 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 DEBUG2(fmt, ...)       if (do_output && (fr_debug_lvl > 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 RDEBUG_ENABLED()       (do_output && (fr_debug_lvl > 0))
+#define RDEBUG_ENABLED2()      (do_output && (fr_debug_lvl > 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__)
+#define RDEBUG(fmt, ...)       if (do_output && (fr_debug_lvl > 0)) fprintf(fr_log_fp, "(%" PRIu64 ") " fmt "\n", request->num, ## __VA_ARGS__)
+#define RDEBUG2(fmt, ...)      if (do_output && (fr_debug_lvl > 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
@@ -71,7 +71,7 @@ struct rc_request {
 
        rc_file_pair_t  *files;         //!< Request and response file names.
 
-       char            password[256];
+       VALUE_PAIR      *password;      //!< Cleartext-Password
        time_t          timestamp;
 
        RADIUS_PACKET   *packet;        //!< The outgoing request.
index 99b7d35..12fa78d 100644 (file)
@@ -5,6 +5,9 @@
  *
  */
 
+/** Internal data types used within libfreeradius
+ *
+ */
 typedef enum {
        PW_TYPE_INVALID = 0,                    //!< Invalid (uninitialised) attribute type.
        PW_TYPE_STRING,                         //!< String of printable characters.
@@ -34,6 +37,9 @@ typedef enum {
        PW_TYPE_MAX                             //!< Number of defined data types.
 } PW_TYPE;
 
+/** RADIUS packet codes
+ *
+ */
 typedef enum {
        PW_CODE_UNDEFINED               = 0,    //!< Packet code has not been set
        PW_CODE_ACCESS_REQUEST          = 1,    //!< RFC2865 - Access-Request
@@ -99,13 +105,12 @@ typedef enum {
 #include <freeradius-devel/rfc7155.h>
 #include <freeradius-devel/rfc7268.h>
 
-
 /*
  *     All internal attributes are now defined in this file.
  */
 #include <freeradius-devel/attributes.h>
 
-#define PW_EXTENDED_ATTRIBUTE          192
+#include <freeradius-devel/vqp.h>
 
 #define PW_DIGEST_RESPONSE             206
 #define PW_DIGEST_ATTRIBUTES           207
@@ -125,54 +130,13 @@ typedef enum {
 #define PW_NAS_PROMPT_USER             7
 #define PW_AUTHENTICATE_ONLY           8
 #define PW_CALLBACK_NAS_PROMPT         9
+#define PW_AUTHORIZE_ONLY              17
 
 /*     Framed Protocols        */
 
 #define PW_PPP                         1
 #define PW_SLIP                                2
 
-/*     Framed Routing Values   */
-
-#define PW_NONE                                0
-#define PW_BROADCAST                   1
-#define PW_LISTEN                      2
-#define PW_BROADCAST_LISTEN            3
-
-/*     Framed Compression Types        */
-
-#define PW_VAN_JACOBSEN_TCP_IP         1
-
-/*     Login Services  */
-
-#define PW_TELNET                      0
-#define PW_RLOGIN                      1
-#define PW_TCP_CLEAR                   2
-#define PW_PORTMASTER                  3
-
-/*     Authentication Level    */
-
-#define PW_AUTHTYPE_LOCAL              0
-#define PW_AUTHTYPE_SYSTEM             1
-#define PW_AUTHTYPE_SECURID            2
-#define PW_AUTHTYPE_CRYPT              3
-#define PW_AUTHTYPE_REJECT             4
-#define PW_AUTHTYPE_ACTIVCARD          5
-#define PW_AUTHTYPE_EAP                        6
-#define PW_AUTHTYPE_ACCEPT             254
-#define PW_AUTHTYPE_MS_CHAP            1028
-
-/* Post-auth types */
-#define PW_POSTAUTHTYPE_LOCAL          0
-#define PW_POSTAUTHTYPE_REJECT         1
-
-/*     Port Types              */
-
-#define PW_NAS_PORT_ASYNC              0
-#define PW_NAS_PORT_SYNC               1
-#define PW_NAS_PORT_ISDN               2
-#define PW_NAS_PORT_ISDN_V120          3
-#define PW_NAS_PORT_ISDN_V110          4
-
 /*     Status Types    */
 
 #define PW_STATUS_START                        1
@@ -208,31 +172,6 @@ typedef enum {
 #define PW_MSCHAP2_CPW                 27
 
 /*
- *     Old nonsense.   Will be deleted ASAP
- */
-#define PW_AUTHTYPE                    1000
-#define PW_AUTZTYPE                    1011
-#define PW_ACCTTYPE                    1012
-#define PW_SESSTYPE                    1013
-#define PW_POSTAUTHTYPE                        1014
-
-/*
- *     Cisco's VLAN Query Protocol.
- */
-#define PW_VQP_PACKET_TYPE             0x2b00
-#define PW_VQP_ERROR_CODE              0x2b01
-#define PW_VQP_SEQUENCE_NUMBER         0x2b02
-
-#define PW_VQP_CLIENT_IP_ADDRESS       0x2c01
-#define PW_VQP_PORT_NAME               0x2c02
-#define PW_VQP_VLAN_NAME               0x2c03
-#define PW_VQP_DOMAIN_NAME             0x2c04
-#define PW_VQP_ETHERNET_FRAME          0x2c05
-#define PW_VQP_MAC                     0x2c06
-#define PW_VQP_UNKNOWN                 0x2c07
-#define PW_VQP_COOKIE                  0x2c08
-
-/*
  * JANET's code for transporting eap channel binding data over ttls
  */
 
index 6fd2167..1123b77 100644 (file)
@@ -63,7 +63,8 @@ typedef struct rad_request REQUEST;
 #include <freeradius-devel/xlat.h>
 #include <freeradius-devel/tmpl.h>
 #include <freeradius-devel/map.h>
-
+#include <freeradius-devel/clients.h>
+#include <freeradius-devel/process.h>
 /*
  *     All POSIX systems should have these headers
  */
@@ -79,89 +80,6 @@ extern "C" {
  */
 typedef struct request_data_t request_data_t;
 
-typedef struct radclient {
-       fr_ipaddr_t             ipaddr;
-       fr_ipaddr_t             src_ipaddr;
-       char const              *longname;
-       char const              *secret;
-       char const              *shortname;
-       bool                    message_authenticator;
-       char const              *nas_type;
-       char const              *login;
-       char const              *password;
-       char const              *server;
-       int                     number; /* internal use only */
-       CONF_SECTION            *cs;
-#ifdef WITH_STATS
-       fr_stats_t              auth;
-#  ifdef WITH_ACCOUNTING
-       fr_stats_t              acct;
-#  endif
-#  ifdef WITH_COA
-       fr_stats_t              coa;
-       fr_stats_t              dsc;
-#  endif
-#endif
-
-       struct timeval          response_window;
-
-       int                     proto;
-#ifdef WITH_TCP
-       fr_socket_limit_t       limit;
-#endif
-#ifdef WITH_TLS
-       bool                    tls_required;
-#endif
-
-#ifdef WITH_DYNAMIC_CLIENTS
-       uint32_t                lifetime;
-       uint32_t                dynamic; /* was dynamically defined */
-       time_t                  created;
-       time_t                  last_new_client;
-       char const              *client_server;
-       bool                    rate_limit;
-#endif
-
-#ifdef WITH_COA
-       char const              *coa_name;
-       home_server_t           *coa_server;
-       home_pool_t             *coa_pool;
-       bool                    defines_coa_server;     //!< Client also defines a home_server.
-#endif
-} RADCLIENT;
-
-/*
- *     Types of listeners.
- *
- *     Ordered by priority!
- */
-typedef enum RAD_LISTEN_TYPE {
-       RAD_LISTEN_NONE = 0,
-#ifdef WITH_PROXY
-       RAD_LISTEN_PROXY,
-#endif
-       RAD_LISTEN_AUTH,
-#ifdef WITH_ACCOUNTING
-       RAD_LISTEN_ACCT,
-#endif
-#ifdef WITH_DETAIL
-       RAD_LISTEN_DETAIL,
-#endif
-#ifdef WITH_VMPS
-       RAD_LISTEN_VQP,
-#endif
-#ifdef WITH_DHCP
-       RAD_LISTEN_DHCP,
-#endif
-#ifdef WITH_COMMAND_SOCKET
-       RAD_LISTEN_COMMAND,
-#endif
-#ifdef WITH_COA
-       RAD_LISTEN_COA,
-#endif
-       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
@@ -183,16 +101,77 @@ typedef enum rlm_rcodes {
 } rlm_rcode_t;
 extern const FR_NAME_NUMBER modreturn_table[];
 
-/*
- *     For listening on multiple IP's and ports.
+/** Main server configuration
+ *
+ * The parsed version of the main server config.
  */
-typedef struct rad_listen_t rad_listen_t;
+typedef struct main_config {
+       struct main_config *next;                       //!< Next version of the main_config.
 
-typedef                void (*fr_request_process_t)(REQUEST *, int);
-/*
- *  Function handler for requests.
- */
-typedef                int (*RAD_REQUEST_FUNP)(REQUEST *);
+       char const      *name;                          //!< Name of the daemon, usually 'radiusd'.
+       CONF_SECTION    *config;                        //!< Root of the server config.
+
+       fr_ipaddr_t     myip;                           //!< IP to bind to. Set on command line.
+       uint16_t        port;                           //!< Port to bind to. Set on command line.
+
+       bool            log_auth;                       //!< Log authentication attempts.
+       bool            log_auth_badpass;               //!< Log successful authentications.
+       bool            log_auth_goodpass;              //!< Log failed authentications.
+       char const      *auth_badpass_msg;              //!< Additional text to append to successful auth messages.
+       char const      *auth_goodpass_msg;             //!< Additional text to append to failed auth messages.
+
+       char const      *denied_msg;                    //!< Additional text to append if the user is already logged
+                                                       //!< in (simultaneous use check failed).
+
+       bool            daemonize;                      //!< Should the server daemonize on startup.
+       char const      *pid_file;                      //!< Path to write out PID file.
+
+#ifdef WITH_PROXY
+       bool            proxy_requests;                 //!< Toggle to enable/disable proxying globally.
+#endif
+       struct timeval  reject_delay;                   //!< How long to wait before sending an Access-Reject.
+       bool            status_server;                  //!< Whether to respond to status-server messages.
+
+
+       uint32_t        max_request_time;               //!< How long a request can be processed for before
+                                                       //!< timing out.
+       uint32_t        cleanup_delay;                  //!< How long before cleaning up cached responses.
+       uint32_t        max_requests;
+
+       uint32_t        debug_level;
+       char const      *log_file;
+       int             syslog_facility;
+
+       char const      *dictionary_dir;                //!< Where to load dictionaries from.
+
+       char const      *checkrad;                      //!< Script to use to determine if a user is already
+                                                       //!< connected.
+
+       rad_listen_t    *listen;                        //!< Head of a linked list of listeners.
+
+
+       char const      *panic_action;                  //!< Command to execute if the server receives a fatal
+                                                       //!< signal.
+
+       struct timeval  init_delay;                     //!< Initial request processing delay.
+
+       uint32_t        talloc_pool_size;               //!< Size of pool to allocate to hold each #REQUEST.
+       bool            debug_memory;                   //!< Cleanup the server properly on exit, freeing
+                                                       //!< up any memory we allocated.
+       bool            memory_report;                  //!< Print a memory report on what's left unfreed.
+                                                       //!< Can only be used when the server is running in single
+                                                       //!< threaded mode.
+
+       bool            allow_core_dumps;               //!< Whether the server is allowed to drop a core when
+                                                       //!< receiving a fatal signal.
+
+       bool            write_pid;                      //!< write the PID file
+
+
+#ifdef ENABLE_OPENSSL_VERSION_CHECK
+       char const      *allow_vulnerable_openssl;      //!< The CVE number of the last security issue acknowledged.
+#endif
+} main_config_t;
 
 #if defined(WITH_VERIFY_PTR)
 #  define VERIFY_REQUEST(_x) verify_request(__FILE__, __LINE__, _x)
@@ -227,44 +206,59 @@ struct rad_request {
        uint32_t                magic;          //!< Magic number used to detect memory corruption,
                                                //!< or request structs that have not been properly initialised.
 #endif
+       unsigned int            number;         //!< Monotonically increasing request number. Reset on server restart.
+       time_t                  timestamp;      //!< When the request was received.
+
+       request_data_t          *data;          //!< Request metadata.
+
+       rad_listen_t            *listener;      //!< The listener that received the request.
+       RADCLIENT               *client;        //!< The client that originally sent us the request.
+
        RADIUS_PACKET           *packet;        //!< Incoming request.
-#ifdef WITH_PROXY
-       RADIUS_PACKET           *proxy;         //!< Outgoing request.
-#endif
+       VALUE_PAIR              *username;      //!< Cached username #VALUE_PAIR from request #RADIUS_PACKET.
+       VALUE_PAIR              *password;      //!< Cached password #VALUE_PAIR from request #RADIUS_PACKET.
+
        RADIUS_PACKET           *reply;         //!< Outgoing response.
+
+       VALUE_PAIR              *config;        //!< #VALUE_PAIR (s) used to set per request parameters
+                                               //!< for modules and the server core at runtime.
+       VALUE_PAIR              *state;         //!< #VALUE_PAIR (s) available over the lifetime of the authentication
+                                               //!< attempt. Useful where the attempt involves a sequence of
+                                               //!< many request/challenge packets, like OTP, and EAP.
+
 #ifdef WITH_PROXY
-       RADIUS_PACKET           *proxy_reply;   //!< Incoming response.
+       rad_listen_t            *proxy_listener;//!< Listener for outgoing requests.
+       RADIUS_PACKET           *proxy;         //!< Outgoing request to proxy server.
+       RADIUS_PACKET           *proxy_reply;   //!< Incoming response from proxy server.
+
+       home_server_t           *home_server;
+       home_pool_t             *home_pool;     //!< For dynamic failover
 #endif
-       VALUE_PAIR              *config_items;  //!< VALUE_PAIRs used to set per request parameters
-                                               //!< for modules and the server core at runtime.
-       VALUE_PAIR              *state;         //!< VALUE_PAIRs used to set session parameters
-                                               //!< for multiple packets, e.g. EAP.
-       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.
 
+       struct timeval          response_delay; //!< How long to wait before sending Access-Rejects.
+       fr_state_action_t       timer_action;   //!< What action to perform when the timer event fires.
+       fr_event_t              *ev;            //!< Event in event loop tied to this request.
+
        RAD_REQUEST_FUNP        handle;         //!< The function to call to move the request through the
                                                //!< various server configuration sections.
+       rlm_rcode_t             rcode;          //!< Last rcode returned by a module
+       char const              *module;        //!< Module the request is currently being processed by.
+       char const              *component;     //!< Section the request is in.
 
-       struct main_config_t    *root;          //!< Pointer to the main config hack to try and deal with hup.
-
-       request_data_t          *data;          //!< Request metadata.
+       int                     delay;
 
-       RADCLIENT               *client;        //!< The client that originally sent us the request.
+       rad_master_state_t      master_state;   //!< Set by the master thread to signal the child that's currently
+                                               //!< working with the request, to do something.
+       rad_child_state_t       child_state;
 
 #ifdef HAVE_PTHREAD_H
        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.
 
-       rad_listen_t            *listener;      //!< The listener that received the request.
-#ifdef WITH_PROXY
-       rad_listen_t            *proxy_listener;//!< Listener for outgoing requests.
-#endif
+       main_config_t           *root;          //!< Pointer to the main config hack to try and deal with hup.
 
-       rlm_rcode_t             rcode;          //!< Last rcode returned by a module
 
        int                     simul_max;      //!< Maximum number of concurrent sessions for this user.
 #ifdef WITH_SESSION_MGMT
@@ -272,29 +266,16 @@ struct rad_request {
        int                     simul_mpp;      //!< WEIRD: 1 is false, 2 is true.
 #endif
 
-       char const              *module;        //!< Module the request is currently being processed by.
-       char const              *component;     //!< Section the request is in.
-
-       int                     delay;
-
-       rad_master_state_t      master_state;
-       rad_child_state_t       child_state;
        RAD_LISTEN_TYPE         priority;
 
-       struct timeval          response_delay;
-       int                     timer_action;
-       fr_event_t              *ev;
-
        bool                    in_request_hash;
 #ifdef WITH_PROXY
        bool                    in_proxy_hash;
 
-       home_server_t           *home_server;
-       home_pool_t             *home_pool;     //!< For dynamic failover
-
        struct timeval          proxy_retransmit;
 
-       uint32_t                num_proxied_requests;
+       uint32_t                num_proxied_requests;   //!< How many times this request was proxied.
+                                                       //!< Retransmissions are driven by requests from the NAS.
        uint32_t                num_proxied_responses;
 #endif
 
@@ -305,7 +286,7 @@ struct rad_request {
                radlog_func_t   func;           //!< Function to call to output log messages about this
                                                //!< request.
 
-               log_lvl_t       lvl;            //!< Request options, currently just holds the debug level or
+               log_lvl_t       lvl;            //!< Controls the verbosity of debug statements regarding
                                                //!< the request.
 
                uint8_t         indent;         //!< By how much to indent log messages. uin8_t so it's obvious
@@ -321,152 +302,14 @@ struct rad_request {
 #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)
-
-typedef struct radclient_list RADCLIENT_LIST;
-
-typedef int (*rad_listen_recv_t)(rad_listen_t *);
-typedef int (*rad_listen_send_t)(rad_listen_t *, REQUEST *);
-typedef int (*rad_listen_print_t)(rad_listen_t const *, char *, size_t);
-typedef int (*rad_listen_encode_t)(rad_listen_t *, REQUEST *);
-typedef int (*rad_listen_decode_t)(rad_listen_t *, REQUEST *);
-
-struct rad_listen_t {
-       struct rad_listen_t *next; /* should be rbtree stuff */
-
-       /*
-        *      For normal sockets.
-        */
-       RAD_LISTEN_TYPE type;
-       int             fd;
-       char const      *server;
-       int             status;
-#ifdef WITH_TCP
-       int             count;
-       bool            dual;
-#endif
-       bool            nodup;
-       bool            synchronous;
-       uint32_t        workers;
+#define RAD_REQUEST_LVL_NONE   (0)             //!< No debug messages should be printed.
+#define RAD_REQUEST_LVL_DEBUG  (1)
+#define RAD_REQUEST_LVL_DEBUG2 (2)
+#define RAD_REQUEST_LVL_DEBUG3 (3)
+#define RAD_REQUEST_LVL_DEBUG4 (4)
 
-#ifdef WITH_TLS
-       fr_tls_server_conf_t *tls;
-#endif
-
-       rad_listen_recv_t recv;
-       rad_listen_send_t send;
-       rad_listen_encode_t encode;
-       rad_listen_decode_t decode;
-       rad_listen_print_t print;
-
-       CONF_SECTION const *cs;
-       void            *data;
-
-#ifdef WITH_STATS
-       fr_stats_t      stats;
-#endif
-};
-
-/*
- *     This shouldn't really be exposed...
- */
-typedef struct listen_socket_t {
-       /*
-        *      For normal sockets.
-        */
-       fr_ipaddr_t     my_ipaddr;
-       uint16_t        my_port;
-
-       char const      *interface;
-#ifdef SO_BROADCAST
-       int             broadcast;
-#endif
-       time_t          rate_time;
-       uint32_t        rate_pps_old;
-       uint32_t        rate_pps_now;
-       uint32_t        max_rate;
-
-       /* for outgoing sockets */
-       home_server_t   *home;
-       fr_ipaddr_t     other_ipaddr;
-       uint16_t        other_port;
-
-       int             proto;
-
-#ifdef WITH_TCP
-       /* for a proxy connecting to home servers */
-       time_t          last_packet;
-       time_t          opened;
-       fr_event_t      *ev;
-
-       fr_socket_limit_t limit;
-
-       struct listen_socket_t *parent;
-       RADCLIENT       *client;
-
-       RADIUS_PACKET   *packet; /* for reading partial packets */
-#endif
-
-#ifdef WITH_TLS
-       tls_session_t   *ssn;
-       REQUEST         *request; /* horrible hacks */
-       VALUE_PAIR      *certs;
-       pthread_mutex_t mutex;
-       uint8_t         *data;
-       size_t          partial;
-#endif
-
-       RADCLIENT_LIST  *clients;
-} listen_socket_t;
-
-#define RAD_LISTEN_STATUS_INIT       (0)
-#define RAD_LISTEN_STATUS_KNOWN      (1)
-#define RAD_LISTEN_STATUS_EOL       (2)
-#define RAD_LISTEN_STATUS_REMOVE_NOW (3)
-
-typedef struct main_config_t {
-       struct main_config *next;
-       fr_ipaddr_t     myip;   /* 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;
-       uint32_t        debug_level;
-       bool            daemonize;
-#ifdef WITH_PROXY
-       bool            proxy_requests;
-#endif
-       struct timeval  reject_delay;
-       bool            status_server;
-#ifdef ENABLE_OPENSSL_VERSION_CHECK
-       char const      *allow_vulnerable_openssl;
-#endif
-
-       uint32_t        max_request_time;
-       uint32_t        cleanup_delay;
-       uint32_t        max_requests;
-       char const      *log_file;
-       char const      *dictionary_dir;
-       char const      *checkrad;
-       char const      *pid_file;
-       rad_listen_t    *listen;
-       int             syslog_facility;
-       CONF_SECTION    *config;
-       char const      *name;
-       char const      *auth_badpass_msg;
-       char const      *auth_goodpass_msg;
-       bool            debug_memory;
-       bool            memory_report;
-       char const      *panic_action;
-       char const      *denied_msg;
-       uint32_t        talloc_pool_size;
-       struct timeval  init_delay; /* initial request processing delay */
-} MAIN_CONFIG_T;
+#define RAD_REQUEST_OPTION_COA (1 << 0)
+#define RAD_REQUEST_OPTION_CTX (1 << 1)
 
 #define SECONDS_PER_DAY                86400
 #define MAX_REQUEST_TIME       30
@@ -500,7 +343,7 @@ typedef enum request_fail {
  *     We really shouldn't have this many.
  */
 extern char const      *progname;
-extern log_lvl_t       debug_flag;
+extern log_lvl_t       rad_debug_lvl;
 extern char const      *radacct_dir;
 extern char const      *radlog_dir;
 extern char const      *radlib_dir;
@@ -546,6 +389,8 @@ int         log_err (char *);
 #define MEM(x) if (!(x)) { ERROR("%s[%u] OUT OF MEMORY", __FILE__, __LINE__); _fr_exit_now(__FILE__, __LINE__, 1); }
 void (*reset_signal(int signo, void (*func)(int)))(int);
 int            rad_mkdir(char *directory, mode_t mode, uid_t uid, gid_t gid);
+size_t         rad_filename_make_safe(UNUSED REQUEST *request, char *out, size_t outlen,
+                                      char const *in, UNUSED void *arg);
 size_t         rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen,
                                    char const *in, UNUSED void *arg);
 ssize_t                rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen);
@@ -566,7 +411,7 @@ int         rad_copy_string_bare(char *dst, char const *src);
 int            rad_copy_variable(char *dst, char const *from);
 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,
+                               int max_argc, char const *argv[], bool can_fail,
                                size_t argv_buflen, char *argv_buf);
 
 void           verify_request(char const *file, int line, REQUEST *request);   /* only for special debug builds */
@@ -608,31 +453,6 @@ int        regex_request_to_sub_named(TALLOC_CTX *ctx, char **out, REQUEST *request, ch
 #  endif
 #endif
 
-/* client.c */
-RADCLIENT_LIST *clients_init(CONF_SECTION *cs);
-void           clients_free(RADCLIENT_LIST *clients);
-RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, bool tls_required);
-void           client_free(RADCLIENT *client);
-bool           client_add(RADCLIENT_LIST *clients, RADCLIENT *client);
-#ifdef WITH_DYNAMIC_CLIENTS
-void           client_delete(RADCLIENT_LIST *clients, RADCLIENT *client);
-RADCLIENT      *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request);
-#endif
-
-RADCLIENT      *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bool with_coa);
-RADCLIENT      *client_afrom_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);
-
-RADCLIENT      *client_findbynumber(RADCLIENT_LIST const *clients,
-                                    int number);
-RADCLIENT      *client_find_old(fr_ipaddr_t const *ipaddr);
-bool           client_add_dynamic(RADCLIENT_LIST *clients, RADCLIENT *master, RADCLIENT *c);
-RADCLIENT      *client_read(char const *filename, int in_server, int flag);
-
-
 /* files.c */
 int            pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int complain);
 void           pairlist_free(PAIR_LIST **);
@@ -662,28 +482,30 @@ pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait,
                           VALUE_PAIR *input_pairs, bool shell_escape);
 int radius_readfrom_program(int fd, pid_t pid, int timeout,
                            char *answer, int left);
-int radius_exec_program(char *out, size_t outlen, VALUE_PAIR **output_pairs,
+int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, VALUE_PAIR **output_pairs,
                        REQUEST *request, char const *cmd, VALUE_PAIR *input_pairs,
-                       bool exec_wait, bool shell_escape, int timeout) CC_HINT(nonnull (4, 5));
+                       bool exec_wait, bool shell_escape, int timeout) CC_HINT(nonnull (5, 6));
 void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quench)
      CC_HINT(nonnull (3));
 
 /* valuepair.c */
+int paircompare_register_byname(char const *name, DICT_ATTR const *from,
+                               bool first_only, RAD_COMPARE_FUNC func, void *instance);
 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      *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *xlat);
-xlat_exp_t             *xlat_from_tmpl_attr(TALLOC_CTX *ctx, value_pair_tmpl_t *vpt);
+vp_tmpl_t      *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *xlat);
+xlat_exp_t             *xlat_from_tmpl_attr(TALLOC_CTX *ctx, vp_tmpl_t *vpt);
 int            radius_xlat_do(REQUEST *request, VALUE_PAIR *vp);
 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(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor);
+VALUE_PAIR     *radius_pair_create(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));
@@ -699,9 +521,9 @@ int radius_copy_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, char con
  * @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)
-#define pairmake_config(_a, _b, _c) pairmake(request, &request->config_items, _a, _b, _c)
+#define pair_make_request(_a, _b, _c) fr_pair_make(request->packet, &request->packet->vps, _a, _b, _c)
+#define pair_make_reply(_a, _b, _c) fr_pair_make(request->reply, &request->reply->vps, _a, _b, _c)
+#define pair_make_config(_a, _b, _c) fr_pair_make(request, &request->config, _a, _b, _c)
 
 /* threads.c */
 int    thread_pool_init(CONF_SECTION *cs, bool *spawn_flag);
@@ -722,8 +544,7 @@ void        thread_pool_queue_stats(int array[RAD_LISTEN_MAX], int pps[2]);
 /* main_config.c */
 /* Define a global config structure */
 extern bool                    log_dates_utc;
-extern bool                    check_config;
-extern struct main_config_t    main_config;
+extern main_config_t           main_config;
 extern bool                    event_loop_started;
 
 void set_radius_dir(TALLOC_CTX *ctx, char const *path);
@@ -736,7 +557,7 @@ void hup_logfile(void);
 /* listen.c */
 void listen_free(rad_listen_t **head);
 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);
+rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, 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
@@ -763,7 +584,7 @@ void mark_home_server_dead(home_server_t *home, struct timeval *when);
 /* evaluate.c */
 typedef struct fr_cond_t fr_cond_t;
 int radius_evaluate_tmpl(REQUEST *request, int modreturn, int depth,
-                        value_pair_tmpl_t const *vpt);
+                        vp_tmpl_t const *vpt);
 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,
index d6fc79d..c909ecd 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This program is 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.
+ *   it under the terms of the GNU General Public License, version 2 of 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
@@ -48,22 +47,22 @@ RCSIDH(radsniff_h, "$Id$")
  *     Logging macros
  */
 #undef DEBUG2
-#define DEBUG2(fmt, ...)       if (fr_debug_flag > 2) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
+#define DEBUG2(fmt, ...)       if (fr_debug_lvl > 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__)
+#define DEBUG(fmt, ...)                if (fr_debug_lvl > 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__)
+#define INFO(fmt, ...)         if (fr_debug_lvl > 0) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
 
 #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 RIDEBUG_ENABLED()      (conf->print_packet && (fr_debug_lvl > 0))
+#define RDEBUG_ENABLED()       (conf->print_packet && (fr_debug_lvl > 1))
+#define RDEBUG_ENABLED2()      (conf->print_packet && (fr_debug_lvl > 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__)
+#define RIDEBUG(fmt, ...)      if (conf->print_packet && (fr_debug_lvl > 0)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+#define RDEBUG(fmt, ...)       if (conf->print_packet && (fr_debug_lvl > 1)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+#define RDEBUG2(fmt, ...)      if (conf->print_packet && (fr_debug_lvl > 2)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
 
 typedef enum {
        RS_NORMAL       = 0x01,
@@ -174,6 +173,8 @@ typedef struct rs_request {
        uint64_t                id;                     //!< Monotonically increasing packet counter.
        fr_event_t              *event;                 //!< Event created when we received the original request.
 
+       bool                    logged;                 //!< Whether any messages regarding this request were logged.
+
        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.
index 379650c..efb7b86 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
index c6a7188..86c3d9f 100644 (file)
@@ -18,22 +18,22 @@ typedef struct {
     uint32_t state[5];
     uint32_t count[2];
     uint8_t buffer[64];
-} fr_SHA1_CTX;
+} fr_sha1_ctx;
 
 void fr_sha1_transform(uint32_t state[5], uint8_t const buffer[64]);
-void fr_sha1_init(fr_SHA1_CTX *context);
-void fr_sha1_update(fr_SHA1_CTX *context, uint8_t const *data, size_t len);
-void fr_sha1_final(uint8_t digest[20], fr_SHA1_CTX *context);
+void fr_sha1_init(fr_sha1_ctx *context);
+void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *data, size_t len);
+void fr_sha1_final(uint8_t digest[20], fr_sha1_ctx *context);
 
 /*
  * this version implements a raw SHA1 transform, no length is appended,
  * nor any 128s out to the block size.
  */
-void fr_sha1_final_no_len(uint8_t digest[20], fr_SHA1_CTX* context);
+void fr_sha1_final_no_len(uint8_t digest[20], fr_sha1_ctx* context);
 
 #else  /* WITH_OPENSSL_SHA1 */
 USES_APPLE_DEPRECATED_API
-#define fr_SHA1_CTX    SHA_CTX
+#define fr_sha1_ctx    SHA_CTX
 #define fr_sha1_init   SHA1_Init
 #define fr_sha1_update SHA1_Update
 #define fr_sha1_final  SHA1_Final
index 9ede142..0484b89 100644 (file)
@@ -30,16 +30,19 @@ RCSIDH(state_h, "$Id$")
 extern "C" {
 #endif
 
-bool fr_state_init(void);
-void fr_state_delete(void);
+typedef struct fr_state_t fr_state_t;
+
+fr_state_t *fr_state_init(TALLOC_CTX *ctx);
+void fr_state_delete(fr_state_t *state);
 
 void fr_state_discard(REQUEST *request, RADIUS_PACKET *original);
 
 void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet);
 bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet);
-void *fr_state_find_data(REQUEST *request, RADIUS_PACKET *packet);
-void *fr_state_get_data(REQUEST *request, RADIUS_PACKET *packet);
-bool fr_state_put_data(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet,
+
+void *fr_state_find_data(fr_state_t *state, REQUEST *request, RADIUS_PACKET *packet);
+void *fr_state_get_data(fr_state_t *state, REQUEST *request, RADIUS_PACKET *packet);
+bool fr_state_put_data(fr_state_t *state, REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet,
                       void *data, void (*free_data)(void *));
 
 #ifdef __cplusplus
index cf3f2a8..624560e 100644 (file)
@@ -26,7 +26,6 @@
 
 RCSIDH(tcp_h, "$Id$")
 
-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);
 #endif /* FR_TCP_H */
index 1c9a59b..4548b7b 100644 (file)
@@ -3,8 +3,7 @@
 /*
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
index a25c19c..a41c6f5 100644 (file)
@@ -49,22 +49,23 @@ extern "C" {
 typedef struct fr_tls_server_conf_t fr_tls_server_conf_t;
 
 typedef enum {
-       FR_TLS_INVALID = 0,             /* invalid, don't reply */
-       FR_TLS_REQUEST,                 /* request, ok to send, invalid to receive */
-       FR_TLS_RESPONSE,                /* response, ok to receive, invalid to send */
-       FR_TLS_SUCCESS,                 /* success, send success */
-       FR_TLS_FAIL,                    /* fail, send fail */
-       FR_TLS_NOOP,                    /* noop, continue */
-
-       FR_TLS_START,                   /* start, ok to send, invalid to receive */
-       FR_TLS_OK,                      /* ok, continue */
-       FR_TLS_ACK,                     /* acknowledge, continue */
-       FR_TLS_FIRST_FRAGMENT,          /* first fragment */
-       FR_TLS_MORE_FRAGMENTS,          /* more fragments, to send/receive */
-       FR_TLS_LENGTH_INCLUDED,         /* length included */
-       FR_TLS_MORE_FRAGMENTS_WITH_LENGTH,   /* more fragments with length */
-       FR_TLS_HANDLED                  /* tls code has handled it */
+       FR_TLS_INVALID = 0,                     //!< Invalid, don't reply.
+       FR_TLS_REQUEST,                         //!< Request, ok to send, invalid to receive.
+       FR_TLS_RESPONSE,                        //!< Response, ok to receive, invalid to send.
+       FR_TLS_SUCCESS,                         //!< Success, send success.
+       FR_TLS_FAIL,                            //!< Fail, send fail.
+       FR_TLS_NOOP,                            //!< Noop, continue.
+
+       FR_TLS_START,                           //!< Start, ok to send, invalid to receive.
+       FR_TLS_OK,                              //!< Ok, continue.
+       FR_TLS_ACK,                             //!< Acknowledge, continue.
+       FR_TLS_FIRST_FRAGMENT,                  //!< First fragment.
+       FR_TLS_MORE_FRAGMENTS,                  //!< More fragments, to send/receive.
+       FR_TLS_LENGTH_INCLUDED,                 //!< Length included.
+       FR_TLS_MORE_FRAGMENTS_WITH_LENGTH,      //!< More fragments with length.
+       FR_TLS_HANDLED                          //!< TLS code has handled it.
 } fr_tls_status_t;
+extern FR_NAME_NUMBER const fr_tls_status_table[];
 
 #define MAX_RECORD_SIZE 16384
 
@@ -88,36 +89,26 @@ typedef enum {
  *     or configure TLS not to exceed MAX_RECORD_SIZE.
  */
 typedef struct _record_t {
-       unsigned char data[MAX_RECORD_SIZE];
-       unsigned int  used;
+       uint8_t data[MAX_RECORD_SIZE];
+       size_t  used;
 } record_t;
 
 typedef struct _tls_info_t {
-       unsigned char   origin;
-       unsigned char   content_type;
-       unsigned char   handshake_type;
-       unsigned char   alert_level;
-       unsigned char   alert_description;
+       int             origin;
+       int             content_type;
+       uint8_t         handshake_type;
+       uint8_t         alert_level;
+       uint8_t         alert_description;
+       bool            initialized;
+
        char            info_description[256];
        size_t          record_len;
        int             version;
-       char            initialized;
 } tls_info_t;
 
-/*
- * tls_session_t Structure gets stored as opaque in eap_handler_t
- * This contains EAP-REQUEST specific data
- * (ie FR_TLS_DATA(fragment), EAPTLS-ALERT, EAPTLS-REQUEST ...)
+/** Contains EAP-REQUEST specific data (ie FR_TLS_DATA(fragment), EAPTLS-ALERT, EAPTLS-REQUEST ...)
  *
- * clean_in  - data that needs to be sent but only after it is soiled.
- * dirty_in  - data EAP server receives.
- * clean_out - data that is cleaned after receiving.
- * dirty_out - data EAP server sends.
- * offset    - current fragment size transmitted
- * fragment  - Flag, In fragment mode or not.
- * tls_msg_len - Actual/Total TLS message length.
- * length_flag - A flag to include length in every TLS Data/Alert packet
- *                                     if set to no then only the first fragment contains length
+ * The tls_session_t Structure gets stored as opaque in eap_handler_t
  */
 typedef struct _tls_session_t {
        SSL_CTX         *ctx;
@@ -126,42 +117,42 @@ typedef struct _tls_session_t {
 
        BIO             *into_ssl;
        BIO             *from_ssl;
-       record_t        clean_in;
-       record_t        clean_out;
-       record_t        dirty_in;
-       record_t        dirty_out;
+       record_t        clean_in;                       //!< Data that needs to be sent but only after it is soiled.
+       record_t        clean_out;                      //!< Data that is cleaned after receiving.
+       record_t        dirty_in;                       //!< Data EAP server receives.
+       record_t        dirty_out;                      //!< Data EAP server sends.
 
        void            (*record_init)(record_t *buf);
        void            (*record_close)(record_t *buf);
-       unsigned int    (*record_plus)(record_t *buf, void const *ptr,
-                                      unsigned int size);
-       unsigned int    (*record_minus)(record_t *buf, void *ptr,
-                                       unsigned int size);
+       unsigned int    (*record_plus)(record_t *buf, void const *ptr, unsigned int size);
+       unsigned int    (*record_minus)(record_t *buf, void *ptr, unsigned int size);
 
-       bool            invalid_hb_used;
+       bool            invalid_hb_used;                //!< Whether heartbleed attack was detected.
 
        /*
-        * Framed-MTU attribute in RADIUS,
-        * if present, can also be used to set this
+        *      Framed-MTU attribute in RADIUS, if present, can also be used to set this
         */
-       unsigned int    offset;
-       unsigned int    tls_msg_len;
-       int             fragment;
-       int             length_flag;
+       size_t          mtu;                            //!< Current fragment size transmitted.
+       size_t          tls_msg_len;                    //!< Actual/Total TLS message length.
+       bool            fragment;                       //!< Flag, In fragment mode or not.
+       bool            length_flag;                    //!< A flag to include length in every TLS Data/Alert packet.
+                                                       //!< If set to no then only the first fragment contains length.
        int             peap_flag;
 
+       size_t          tls_record_in_total_len;        //!< How long the peer indicated the complete tls record
+                                                       //!< would be.
+       size_t          tls_record_in_recvd_len;        //!< How much of the record we've received so far.
+
        /*
-        *      Used by TTLS & PEAP to keep track of other per-session
-        *      data.
+        *      Used by TTLS & PEAP to keep track of other per-session data.
         */
        void            *opaque;
        void            (*free_opaque)(void *opaque);
 
        char const      *prf_label;
-       int             allow_session_resumption;
+       bool            allow_session_resumption;       //!< Whether session resumption is allowed.
 } tls_session_t;
 
-
 /*
  *     RFC 2716, Section 4.2:
  *
@@ -316,10 +307,6 @@ void tls_fail(tls_session_t *ssn);
 fr_tls_status_t tls_ack_handler(tls_session_t *tls_session, REQUEST *request);
 fr_tls_status_t tls_application_data(tls_session_t *ssn, REQUEST *request);
 
-/* Session */
-void           session_close(tls_session_t *ssn);
-void           session_init(tls_session_t *ssn);
-
 #define FR_TLS_EX_INDEX_HANDLER  (10)
 #define FR_TLS_EX_INDEX_CONF    (11)
 #define FR_TLS_EX_INDEX_REQUEST         (12)
@@ -329,6 +316,7 @@ void                session_init(tls_session_t *ssn);
 #define FR_TLS_EX_INDEX_TALLOC  (16)
 
 extern int fr_tls_ex_index_certs;
+extern int fr_tls_ex_index_vps;
 
 /* configured values goes right here */
 struct fr_tls_server_conf_t {
@@ -350,6 +338,7 @@ struct fr_tls_server_conf_t {
        uint32_t        verify_depth;
        bool            file_type;
        bool            include_length;
+       bool            disable_tlsv1;
        bool            disable_tlsv1_1;
        bool            disable_tlsv1_2;
 
@@ -358,6 +347,7 @@ struct fr_tls_server_conf_t {
         */
        uint32_t        fragment_size;
        bool            check_crl;
+       bool            check_all_crl;
        bool            allow_expired_crl;
        char const      *check_cert_cn;
        char const      *cipher_list;
index 9935059..4392cd1 100644 (file)
  * @file tmpl.h
  * @brief Structures and prototypes for templates
  *
- * @copyright 2014  The FreeRADIUS server project
+ * These functions are used to work with #vp_tmpl_t structs.
+ *
+ * #vp_tmpl_t (VPTs) specify either a data source, or a data sink.
+ *
+ * Examples of sources are #TMPL_TYPE_XLAT, #TMPL_TYPE_EXEC and #TMPL_TYPE_ATTR.
+ * Examples of sinks are #TMPL_TYPE_ATTR, #TMPL_TYPE_LIST.
+ *
+ * VPTs are used to gather values or attributes for evaluation, or copying, and to specify
+ * where values or #VALUE_PAIR should be copied to.
+ *
+ * To create new #vp_tmpl_t use one of the tmpl_*from_* functions.  These parse
+ * strings into VPTs. The main parsing function is #tmpl_afrom_str, which can produce
+ * most types of VPTs. It uses the type of quoting (passed as an #FR_TOKEN) to determine
+ * what type of VPT to parse the string as. For example a #T_DOUBLE_QUOTED_STRING will
+ * produce either a #TMPL_TYPE_XLAT or a #TMPL_TYPE_LITERAL (depending if the string
+ * contained a non-literal expansion).
+ *
+ * @see tmpl_afrom_str
+ * @see tmpl_afrom_attr_str
+ * @see tmpl_from_attr_str
+ * @see tmpl_from_attr_substr
+ *
+ * In the case of #TMPL_TYPE_ATTR and #TMPL_TYPE_LIST, there are special cursor overlay
+ * functions which can be used to iterate over only the #VALUE_PAIR that match a
+ * vp_tmpl_t in a given list.
+ *
+ * @see tmpl_cursor_init
+ * @see tmpl_cursor_next
+ *
+ * Or for simplicity, there are functions which wrap the cursor functions, to copy or
+ * return the #VALUE_PAIR that match the VPT.
+ *
+ * @see tmpl_copy_vps
+ * @see tmpl_find_vp
+ *
+ * If you just need the string value of whatever the VPT refers to, the tmpl_*expand
+ * functions may be used. These functions evaluate the VPT, execing, and xlat expanding
+ * as necessary. In the case of #TMPL_TYPE_ATTR, and #PW_TYPE_STRING or #PW_TYPE_OCTETS
+ * #tmpl_expand will return a pointer to the raw #VALUE_PAIR buffer. This can be very
+ * useful when using the #PW_TYPE_TMPL type in #CONF_PARSER structs, as it allows the
+ * user to determine whether they want the module to sanitise the value using presentation
+ * format specific #xlat_escape_t function, or to operate on the raw value.
+ *
+ * @see tmpl_expand
+ * @see tmpl_aexpand
+ *
+ * @copyright 2014-2015 The FreeRADIUS server project
  */
 
 RCSIDH(tmpl_h, "$Id$")
@@ -31,32 +77,42 @@ RCSIDH(tmpl_h, "$Id$")
 #ifdef __cplusplus
 extern "C" {
 #endif
-
+#
 typedef enum pair_lists {
-       PAIR_LIST_UNKNOWN = 0,
-       PAIR_LIST_REQUEST,
-       PAIR_LIST_REPLY,
-       PAIR_LIST_CONTROL,
-       PAIR_LIST_STATE,
+       PAIR_LIST_UNKNOWN = 0,          //!< Unknown list.
+       PAIR_LIST_REQUEST,              //!< Attributes in incoming or internally proxied
+                                       ///< request.
+       PAIR_LIST_REPLY,                //!< Attributes to send in the response.
+       PAIR_LIST_CONTROL,              //!< Attributes that change the behaviour of
+                                       ///< modules.
+       PAIR_LIST_STATE,                //!< Attributes to store multiple rounds of
+                                       ///< challenges/responses.
 #ifdef WITH_PROXY
-       PAIR_LIST_PROXY_REQUEST,
-       PAIR_LIST_PROXY_REPLY,
+       PAIR_LIST_PROXY_REQUEST,        //!< A copy of attributes in the request list
+                                       ///< that may be modified in pre-proxy before
+                                       //!< proxying the request.
+       PAIR_LIST_PROXY_REPLY,          //!< Attributes sent in response to the proxied
+                                       ///< request.
 #endif
 #ifdef WITH_COA
-       PAIR_LIST_COA,
-       PAIR_LIST_COA_REPLY,
-       PAIR_LIST_DM,
-       PAIR_LIST_DM_REPLY
+       PAIR_LIST_COA,                  //!< Attributes to send in a forked CoA-Request.
+       PAIR_LIST_COA_REPLY,            //!< Attributes sent in response to the forked
+                                       ///< CoA-Request.
+       PAIR_LIST_DM,                   //!< Attributes to send in a forked Disconnect-Request.
+       PAIR_LIST_DM_REPLY              //!< Attributes sent in response to the forked
+                                       //!< Disconnect-Request.
 #endif
 } pair_lists_t;
 
 extern const FR_NAME_NUMBER pair_lists[];
 
 typedef enum requests {
-       REQUEST_UNKNOWN = 0,
-       REQUEST_OUTER,
-       REQUEST_CURRENT,
-       REQUEST_PARENT  /* For future use */
+       REQUEST_UNKNOWN = 0,            //!< Unknown request.
+       REQUEST_OUTER,                  //!< #REQUEST containing the outer layer of the EAP
+                                       //!< conversation. Usually the RADIUS request sent
+                                       //!< by the NAS.
+       REQUEST_CURRENT,                //!< The current request.
+       REQUEST_PARENT                  //!< Not currently used.
 } request_refs_t;
 
 extern const FR_NAME_NUMBER request_refs[];
@@ -66,28 +122,30 @@ typedef struct pair_list {
        VALUE_PAIR              *check;
        VALUE_PAIR              *reply;
        int                     lineno;
-       int                     order;
        struct pair_list        *next;
-       struct pair_list        *lastdefault;
 } PAIR_LIST;
 
+/** Types of #vp_tmpl_t
+ */
 typedef enum tmpl_type {
-       TMPL_TYPE_UNKNOWN = 0,
-       TMPL_TYPE_LITERAL,              //!< Is a literal string.
-       TMPL_TYPE_XLAT,                 //!< Needs to be expanded.
-       TMPL_TYPE_ATTR,                 //!< Is a dictionary attribute.
-       TMPL_TYPE_ATTR_UNDEFINED,       //!< Is an attribute not found in the global dictionary.
-       TMPL_TYPE_LIST,                 //!< Is a list.
-       TMPL_TYPE_REGEX,                //!< Is a regex.
-       TMPL_TYPE_EXEC,                 //!< Needs to be executed.
-       TMPL_TYPE_DATA,                 //!< Is a value_data_t
-       TMPL_TYPE_XLAT_STRUCT,          //!< pre-parsed xlat_exp_t
-       TMPL_TYPE_REGEX_STRUCT,         //!< pre-parsed regex_t
-       TMPL_TYPE_NULL                  //!< VPT has no value
+       TMPL_TYPE_UNKNOWN = 0,          //!< Uninitialised.
+       TMPL_TYPE_LITERAL,              //!< Literal string.
+       TMPL_TYPE_XLAT,                 //!< XLAT expansion.
+       TMPL_TYPE_ATTR,                 //!< Dictionary attribute.
+       TMPL_TYPE_ATTR_UNDEFINED,       //!< Attribute not found in the global dictionary.
+       TMPL_TYPE_LIST,                 //!< Attribute list.
+       TMPL_TYPE_REGEX,                //!< Regular expression.
+       TMPL_TYPE_EXEC,                 //!< Callout to an external script or program.
+       TMPL_TYPE_DATA,                 //!< Value in native format.
+       TMPL_TYPE_XLAT_STRUCT,          //!< Pre-parsed XLAT expansion.
+       TMPL_TYPE_REGEX_STRUCT,         //!< Pre-parsed regular expression.
+       TMPL_TYPE_NULL                  //!< Has no value.
 } tmpl_type_t;
 
 extern const FR_NAME_NUMBER tmpl_names[];
 
+/** Describes a #TMPL_TYPE_ATTR, #TMPL_TYPE_ATTR_UNDEFINED or #TMPL_TYPE_LIST
+ */
 typedef struct {
        request_refs_t          request;                //!< Request to search or insert in.
        pair_lists_t            list;                   //!< List to search or insert in.
@@ -101,36 +159,40 @@ typedef struct {
        int8_t                  tag;                     //!< For tag references.
 } value_pair_tmpl_attr_t;
 
-/** A pre-parsed template attribute
+/** A source or sink of value data.
  *
  * Is used as both the RHS and LHS of a map (both update, and conditional types)
  *
+ * @section update_maps Use in update vp_map_t
  * When used on the LHS it describes an attribute to create and should be one of these types:
- * - TMPL_TYPE_ATTR
- * - TMPL_TYPE_LIST
+ * - #TMPL_TYPE_ATTR
+ * - #TMPL_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:
- * - TMPL_TYPE_LITERAL
- * - TMPL_TYPE_XLAT
- * - TMPL_TYPE_ATTR
- * - TMPL_TYPE_LIST
- * - TMPL_TYPE_EXEC
- * - TMPL_TYPE_DATA
- * - TMPL_TYPE_XLAT_STRUCT (pre-parsed xlat)
+ * - #TMPL_TYPE_LITERAL
+ * - #TMPL_TYPE_XLAT
+ * - #TMPL_TYPE_ATTR
+ * - #TMPL_TYPE_LIST
+ * - #TMPL_TYPE_EXEC
+ * - #TMPL_TYPE_DATA
+ * - #TMPL_TYPE_XLAT_STRUCT (pre-parsed xlat)
  *
+ * @section conditional_maps Use in conditional vp_map_t
  * When used as part of a condition it may be any of the RHS side types, as well as:
- * - TMPL_TYPE_REGEX_STRUCT (pre-parsed regex)
+ * - #TMPL_TYPE_REGEX_STRUCT (pre-parsed regex)
  *
- * @see value_pair_map_t
+ * @see vp_map_t
  */
-typedef struct value_pair_tmpl_t {
+typedef struct vp_tmpl_t {
        tmpl_type_t     type;           //!< What type of value tmpl refers to.
        char const      *name;          //!< Original attribute ref string, or
                                        //!< where this refers to a none FR
                                        //!< attribute, just the string id for
                                        //!< the attribute.
        size_t          len;            //!< Name length.
+       char            quote;          //!< Quotation character for "name"
+       bool            auto_converted; //!< Attr-26.9.1 --> Cisco-AVPair
 
 #ifdef HAVE_REGEX
        bool            iflag;          //!< regex - case insensitive (if operand is used in regex comparison)
@@ -156,11 +218,15 @@ typedef struct value_pair_tmpl_t {
                xlat_exp_t      *xlat;   //!< pre-parsed xlat_exp_t
 
 #ifdef HAVE_REGEX
-               regex_t         *preg;          //!< pre-parsed regex_t
+               regex_t         *preg;  //!< pre-parsed regex_t
 #endif
        } data;
-} value_pair_tmpl_t;
+} vp_tmpl_t;
 
+/** @name Field accessors for #TMPL_TYPE_ATTR, #TMPL_TYPE_ATTR_UNDEFINED, #TMPL_TYPE_LIST
+ *
+ * @{
+ */
 #define tmpl_request           data.attribute.request
 #define tmpl_list              data.attribute.list
 #define tmpl_da                        data.attribute.da
@@ -168,28 +234,43 @@ typedef struct value_pair_tmpl_t {
 #define tmpl_unknown_name              data.attribute.unknown.name
 #define tmpl_num               data.attribute.num
 #define tmpl_tag               data.attribute.tag
+/* @} **/
 
+/** @name Field accessors for #TMPL_TYPE_XLAT_STRUCT
+ *
+ * @{
+ */
 #define tmpl_xlat              data.xlat
+/* @} **/
 
+/** @name Field accessors for #TMPL_TYPE_DATA
+ *
+ * @{
+ */
 #define tmpl_data              data.literal
 #define tmpl_data_type         data.literal.type
 #define tmpl_data_length       data.literal.length
 #define tmpl_data_value                data.literal.data
+/* @} **/
 
+/** @name Field accessors for #TMPL_TYPE_REGEX_STRUCT and #TMPL_TYPE_REGEX
+ *
+ * @{
+ */
 #ifdef HAVE_REGEX
-#  define tmpl_preg            data.preg
+#  define tmpl_preg            data.preg       //!< #TMPL_TYPE_REGEX_STRUCT only.
 #  define tmpl_iflag           iflag
 #  define tmpl_mflag           mflag
 #endif
+/* @} **/
 
 #ifndef WITH_VERIFY_PTR
 #  define VERIFY_TMPL(_x)
 #else
 #  define VERIFY_TMPL(_x) tmpl_verify(__FILE__,  __LINE__, _x)
-void tmpl_verify(char const *file, int line, value_pair_tmpl_t const *vpt);
+void tmpl_verify(char const *file, int line, vp_tmpl_t const *vpt);
 #endif
 
-/* Attribute qualifier parsing */
 VALUE_PAIR             **radius_list(REQUEST *request, pair_lists_t list);
 
 RADIUS_PACKET          *radius_packet(REQUEST *request, pair_lists_t list_name);
@@ -202,70 +283,60 @@ int                       radius_request(REQUEST **request, request_refs_t name);
 
 size_t                 radius_request_name(request_refs_t *out, char const *name, request_refs_t unknown);
 
-
-/* Template manipulation and execution */
-value_pair_tmpl_t      *tmpl_init(value_pair_tmpl_t *vpt, tmpl_type_t type,
+vp_tmpl_t              *tmpl_init(vp_tmpl_t *vpt, tmpl_type_t type,
                                   char const *name, ssize_t len);
 
-value_pair_tmpl_t      *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name,
+vp_tmpl_t              *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name,
                                    ssize_t len);
 
-/*
- *     The following three functions parse attribute name strings into templates
- *
- *     The 'str' variants will error out if the entire string isn't parsed.
- *     The 'afrom' variant will alloc a new tmpl structure.
- *
- */
-ssize_t                        tmpl_from_attr_substr(value_pair_tmpl_t *vpt, char const *name,
+ssize_t                        tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name,
                                              request_refs_t request_def, pair_lists_t list_def,
                                              bool allow_unknown, bool allow_undefined);
 
-ssize_t                        tmpl_from_attr_str(value_pair_tmpl_t *vpt, char const *name,
+ssize_t                        tmpl_from_attr_str(vp_tmpl_t *vpt, char const *name,
                                           request_refs_t request_def,
                                           pair_lists_t list_def,
                                           bool allow_unknown, bool allow_undefined);
 
-ssize_t                        tmpl_afrom_attr_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name,
+ssize_t                        tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name,
+                                              request_refs_t request_def, pair_lists_t list_def,
+                                              bool allow_unknown, bool allow_undefined);
+
+ssize_t                        tmpl_afrom_attr_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name,
                                            request_refs_t request_def,
                                            pair_lists_t list_def,
                                            bool allow_unknown, bool allow_undefined);
 
-/*
- *     Parses any type of string into a template
- */
-ssize_t                        tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name, size_t inlen,
-                                      FR_TOKEN type, request_refs_t request_def, pair_lists_t list_def);
+ssize_t                        tmpl_afrom_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name, size_t inlen,
+                                      FR_TOKEN type, request_refs_t request_def, pair_lists_t list_def, bool do_escape);
 
-void                   tmpl_free(value_pair_tmpl_t **tmpl);
+int                    tmpl_cast_in_place(vp_tmpl_t *vpt, PW_TYPE type, DICT_ATTR const *enumv);
 
-bool                   tmpl_cast_in_place(value_pair_tmpl_t *vpt, PW_TYPE type, DICT_ATTR const *enumv);
+void                   tmpl_cast_in_place_str(vp_tmpl_t *vpt);
 
-void                   tmpl_cast_in_place_str(value_pair_tmpl_t *vpt);
+int                    tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request,
+                                       vp_tmpl_t const *vpt, DICT_ATTR const *cast);
 
-size_t                 tmpl_prints(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt,
+size_t                 tmpl_prints(char *buffer, size_t bufsize, vp_tmpl_t const *vpt,
                                    DICT_ATTR const *values);
 
-int                    tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request,
-                                       value_pair_tmpl_t const *vpt, DICT_ATTR const *cast);
-
 ssize_t                        tmpl_expand(char const **out, char *buff, size_t outlen, REQUEST *request,
-                                   value_pair_tmpl_t const *vpt, RADIUS_ESCAPE_STRING escape, void *escape_ctx);
+                                   vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx);
 
-ssize_t                        tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, value_pair_tmpl_t const *vpt,
-                                    RADIUS_ESCAPE_STRING escape, void *escape_ctx);
+ssize_t                        tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, vp_tmpl_t const *vpt,
+                                    xlat_escape_t escape, void *escape_ctx);
 
 VALUE_PAIR             *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request,
-                                         value_pair_tmpl_t const *vpt);
+                                         vp_tmpl_t const *vpt);
 
-VALUE_PAIR             *tmpl_cursor_next(vp_cursor_t *cursor, value_pair_tmpl_t const *vpt);
+VALUE_PAIR             *tmpl_cursor_next(vp_cursor_t *cursor, vp_tmpl_t const *vpt);
 
 int                    tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request,
-                                     value_pair_tmpl_t const *vpt);
+                                     vp_tmpl_t const *vpt);
 
-int                    tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt);
+int                    tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, vp_tmpl_t const *vpt);
 
-bool                   tmpl_define_unknown_attr(value_pair_tmpl_t *vpt);
+int                    tmpl_define_unknown_attr(vp_tmpl_t *vpt);
 
 #ifdef __cplusplus
 }
index 8b8f13f..535dd81 100644 (file)
@@ -35,21 +35,21 @@ extern "C" {
 
 typedef struct xlat_exp xlat_exp_t;
 
-typedef size_t (*RADIUS_ESCAPE_STRING)(REQUEST *, char *out, size_t outlen, char const *in, void *arg);
-typedef ssize_t (*RAD_XLAT_FUNC)(void *instance, REQUEST *, char const *, char *, size_t);
+typedef size_t (*xlat_escape_t)(REQUEST *, char *out, size_t outlen, char const *in, void *arg);
+typedef ssize_t (*xlat_func_t)(void *instance, REQUEST *, char const *, char *, size_t);
 
-ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape,
+ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape,
                    void *escape_ctx)
        CC_HINT(nonnull (1 ,3 ,4));
 
 ssize_t radius_xlat_struct(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat,
-                          RADIUS_ESCAPE_STRING escape, void *ctx)
+                          xlat_escape_t escape, void *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)
+ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, xlat_escape_t escape, void *escape_ctx)
        CC_HINT(nonnull (1, 2, 3));
 
-ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, RADIUS_ESCAPE_STRING escape,
+ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape,
                            void *ctx)
        CC_HINT(nonnull (1, 2, 3));
 
@@ -57,9 +57,9 @@ ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const
 
 size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node);
 
-int            xlat_register(char const *module, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape,
+int            xlat_register(char const *module, xlat_func_t func, xlat_escape_t escape,
                              void *instance);
-void           xlat_unregister(char const *module, RAD_XLAT_FUNC func, void *instance);
+void           xlat_unregister(char const *module, xlat_func_t func, void *instance);
 void           xlat_unregister_module(void *instance);
 bool           xlat_register_redundant(CONF_SECTION *cs);
 ssize_t                xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt);
index c323438..471b424 100644 (file)
@@ -30,6 +30,7 @@ SOURCES               := cbuff.c \
                   snprintf.c \
                   strlcat.c \
                   strlcpy.c \
+                  socket.c \
                   token.c \
                   udpfromto.c \
                   value.c \
index 85dd94d..7e33a94 100644 (file)
@@ -218,7 +218,7 @@ static const signed char b64[0x100] = {
  * @return true if CH is a character from the Base64 alphabet, and false
  *     otherwise.
  */
-int fr_isbase64(char c)
+bool fr_is_base64(char c)
 {
        return b64[us(c)] >= 0;
 }
@@ -253,7 +253,7 @@ ssize_t fr_base64_decode(uint8_t *out, size_t outlen, char const *in, size_t inl
        }
 
        while (inlen >= 2) {
-               if (!fr_isbase64(in[0]) || !fr_isbase64(in[1])) {
+               if (!fr_is_base64(in[0]) || !fr_is_base64(in[1])) {
                        break;
                }
 
@@ -264,7 +264,7 @@ ssize_t fr_base64_decode(uint8_t *out, size_t outlen, char const *in, size_t inl
                if (in[2] == '=') {
                        if ((inlen != 4) || (in[3] != '=')) break;
                } else {
-                       if (!fr_isbase64(in[2])) break;
+                       if (!fr_is_base64(in[2])) break;
 
                        *p++ = ((b64[us(in[1])] << 4) & 0xf0) | (b64[us(in[2])] >> 2);
 
@@ -273,7 +273,7 @@ ssize_t fr_base64_decode(uint8_t *out, size_t outlen, char const *in, size_t inl
                        if (in[3] == '=') {
                                if (inlen != 4) break;
                        } else {
-                               if (!fr_isbase64(in[3])) break;
+                               if (!fr_is_base64(in[3])) break;
 
                                *p++ = ((b64[us(in[2])] << 6) & 0xc0) | b64[us(in[3])];
                        }
index 94a1b09..89cf34c 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This program is 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.
+ *   it under the terms of the GNU General Public License, version 2 of 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
 
 /**
  * $Id$
+ *
  * @file cursor.c
  * @brief Functions to iterate over collections of VALUE_PAIRs
  *
+ * @note Do not modify collections of VALUE_PAIRs pointed to be a cursor
+ *      with none fr_cursor_* functions, during the lifetime of that cursor.
+ *
  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
- * @copyright 2013 The FreeRADIUS Server Project.
+ * @copyright 2013-2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013-2015 The FreeRADIUS Server Project.
  */
 
 #include <freeradius-devel/libradius.h>
 
+/** Internal function to update cursor state
+ *
+ * @param cursor to operate on.
+ * @param vp to set current and found positions to.
+ * @return value passed in as vp.
+ */
+inline static VALUE_PAIR *fr_cursor_update(vp_cursor_t *cursor, VALUE_PAIR *vp)
+{
+       if (!vp) {
+               cursor->next = NULL;
+               cursor->current = NULL;
+
+               return NULL;
+       }
+
+       cursor->next = vp->next;
+       cursor->current = vp;
+       cursor->found = vp;
+
+       return vp;
+}
+
 /** Setup a cursor to iterate over attribute pairs
  *
  * @param cursor Where to initialise the cursor (uses existing structure).
- * @param node to start from.
+ * @param const_vp to start from.
+ * @return the attribute pointed to by vp.
  */
-VALUE_PAIR *_fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR const * const *node)
+VALUE_PAIR *fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR * const *const_vp)
 {
-       if (!node || !cursor) {
+       VALUE_PAIR **vp;
+
+       if (!const_vp || !cursor) {
                return NULL;
        }
 
        memset(cursor, 0, sizeof(*cursor));
 
+       memcpy(&vp, &const_vp, sizeof(vp)); /* stupid const hacks */
+
        /*
         *  Useful check to see if uninitialised memory is pointed
-        *  to by node
+        *  to by vp
         */
 #ifndef NDEBUG
-       if (*node) VERIFY_VP(*node);
+       if (*vp) VERIFY_VP(*vp);
 #endif
-       memcpy(&cursor->first, &node, sizeof(cursor->first));
+       memcpy(&cursor->first, &vp, sizeof(cursor->first));
        cursor->current = *cursor->first;
 
        if (cursor->current) {
@@ -56,11 +87,21 @@ VALUE_PAIR *_fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR const * const *node)
        return cursor->current;
 }
 
+/** Copy a cursor
+ *
+ * @param in Cursor to copy.
+ * @param out Where to copy the cursor to.
+ */
 void fr_cursor_copy(vp_cursor_t *out, vp_cursor_t *in)
 {
        memcpy(out, in, sizeof(*out));
 }
 
+/** Rewind cursor to the start of the list
+ *
+ * @param cursor to operate on.
+ * @return the VALUE_PAIR at the start of the list.
+ */
 VALUE_PAIR *fr_cursor_first(vp_cursor_t *cursor)
 {
        if (!cursor->first) return NULL;
@@ -77,8 +118,10 @@ VALUE_PAIR *fr_cursor_first(vp_cursor_t *cursor)
        return cursor->current;
 }
 
-/** Return the last pair in the list
+/** Wind cursor to the last pair in the list
  *
+ * @param cursor to operate on.
+ * @return the VALUE_PAIR at the end of the list.
  */
 VALUE_PAIR *fr_cursor_last(vp_cursor_t *cursor)
 {
@@ -93,25 +136,20 @@ VALUE_PAIR *fr_cursor_last(vp_cursor_t *cursor)
        return cursor->current;
 }
 
-static VALUE_PAIR *fr_cursor_update(vp_cursor_t *cursor, VALUE_PAIR *i)
-{
-       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 type in the pairlist
+/** Iterate over a collection of VALUE_PAIRs of a given type in the pairlist
  *
+ * Find the next attribute of a given type. If no fr_cursor_next_by_* function
+ * has been called on a cursor before, or the previous call returned
+ * NULL, the search will start with the current attribute. Subsequent calls to
+ * fr_cursor_next_by_* functions will start the search from the previously
+ * matched attribute.
  *
+ * @param cursor to operate on.
+ * @param attr number to match.
+ * @param vendor number to match (0 for none vendor attribute).
+ * @param tag to match. Either a tag number or TAG_ANY to match any tagged or
+ *       untagged attribute, TAG_NONE to match attributes without tags.
+ * @return the next matching VALUE_PAIR, or NULL if no VALUE_PAIRs match.
  */
 VALUE_PAIR *fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int attr, unsigned int vendor, int8_t tag)
 {
@@ -134,7 +172,19 @@ VALUE_PAIR *fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int attr, unsign
 
 /** Iterate over attributes of a given DA in the pairlist
  *
+ * Find the next attribute of a given type. If no fr_cursor_next_by_* function
+ * has been called on a cursor before, or the previous call returned
+ * NULL, the search will start with the current attribute. Subsequent calls to
+ * fr_cursor_next_by_* functions will start the search from the previously
+ * matched attribute.
+ *
+ * @note DICT_ATTR pointers are compared, not the attribute numbers and vendors.
  *
+ * @param cursor to operate on.
+ * @param da to match.
+ * @param tag to match. Either a tag number or TAG_ANY to match any tagged or
+ *       untagged attribute, TAG_NONE to match attributes without tags.
+ * @return the next matching VALUE_PAIR, or NULL if no VALUE_PAIRs match.
  */
 VALUE_PAIR *fr_cursor_next_by_da(vp_cursor_t *cursor, DICT_ATTR const *da, int8_t tag)
 {
@@ -155,9 +205,10 @@ VALUE_PAIR *fr_cursor_next_by_da(vp_cursor_t *cursor, DICT_ATTR const *da, int8_
        return fr_cursor_update(cursor, i);
 }
 
-/** Retrieve the next VALUE_PAIR
- *
+/** Advanced the cursor to the next VALUE_PAIR
  *
+ * @param cursor to operate on.
+ * @return the next VALUE_PAIR, or NULL if no more VALUE_PAIRS in the collection.
  */
 VALUE_PAIR *fr_cursor_next(vp_cursor_t *cursor)
 {
@@ -183,14 +234,21 @@ VALUE_PAIR *fr_cursor_next(vp_cursor_t *cursor)
        return cursor->current;
 }
 
-/** Return what's coming next without advancing the cursor
+/** Return the next VALUE_PAIR without advancing the cursor
  *
+ * @param cursor to operate on.
+ * @return the next VALUE_PAIR, or NULL if no more VALUE_PAIRS in the collection.
  */
 VALUE_PAIR *fr_cursor_next_peek(vp_cursor_t *cursor)
 {
        return cursor->next;
 }
 
+/** Return the VALUE_PAIR the cursor current points to
+ *
+ * @param cursor to operate on.
+ * @return the VALUE_PAIR the cursor currently points to.
+ */
 VALUE_PAIR *fr_cursor_current(vp_cursor_t *cursor)
 {
        if (cursor->current) VERIFY_VP(cursor->current);
@@ -198,32 +256,37 @@ VALUE_PAIR *fr_cursor_current(vp_cursor_t *cursor)
        return cursor->current;
 }
 
-/** Insert a single VP at the end of the list
+/** Insert a single VALUE_PAIR at the end of the list
+ *
+ * @note Will not advance cursor position to new attribute, but will set cursor
+ *      to this attribute, if it's the first one in the list.
  *
- * @todo don't use with pairdelete
+ * Insert a VALUE_PAIR at the end of the list.
+ *
+ * @param cursor to operate on.
+ * @param vp to insert.
  */
-void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
+void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
 {
        VALUE_PAIR *i;
 
        if (!fr_assert(cursor->first)) return;  /* cursor must have been initialised */
 
-       if (!add) return;
+       if (!vp) return;
 
-       VERIFY_VP(add);
+       VERIFY_VP(vp);
 
        /*
         *      Only allow one VP to by inserted at a time
         */
-       add->next = NULL;
+       vp->next = NULL;
 
        /*
         *      Cursor was initialised with a pointer to a NULL value_pair
         */
-
        if (!*cursor->first) {
-               *cursor->first = add;
-               cursor->current = add;
+               *cursor->first = vp;
+               cursor->current = vp;
 
                return;
        }
@@ -231,14 +294,15 @@ void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
        /*
         *      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.
+        *      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
+        *      Wind last to the end of the list.
         */
        if (cursor->last->next) {
                for (i = cursor->last; i; i = i->next) {
@@ -248,28 +312,29 @@ void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
        }
 
        /*
-        *      Either current was never set, or something iterated to the end of the
-        *      attribute list.
+        *      Either current was never set, or something iterated to the
+        *      end of the attribute list. In both cases the newly inserted
+        *      VALUE_PAIR should be set as the current VALUE_PAIR.
         */
-       if (!cursor->current) {
-               cursor->current = add;
-       }
+       if (!cursor->current) cursor->current = vp;
 
        /*
-        *      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.
+        *      Add the VALUE_PAIR to the end of the list
         */
-       if (!cursor->next) {
-               cursor->next = add->next;
-       }
+       cursor->last->next = vp;
+       cursor->last = vp;      /* Wind it forward a little more */
 
-       cursor->last->next = add;
+       /*
+        *      If the next pointer was NULL, and the VALUE_PAIR
+        *      just added has a next pointer value, set the cursor's next
+        *      pointer to the VALUE_PAIR's next pointer.
+        */
+       if (!cursor->next) cursor->next = cursor->current->next;
 }
 
-/** Merges two sets of VPs
+/** Merges multiple VALUE_PAIR into the cursor
  *
- * The list represented by cursor will hold the union of cursor and
- * add lists.
+ * Add multiple VALUE_PAIR from add to cursor.
  *
  * @param cursor to insert VALUE_PAIRs with
  * @param add one or more VALUE_PAIRs (may be NULL, which results in noop).
@@ -294,33 +359,68 @@ void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *add)
  *
  * @todo this is really inefficient and should be fixed...
  *
+ * The current VP will be set to the one before the VP being removed,
+ * this is so the commonly used check and remove loop (below) works
+ * as expected.
+ @code {.c}
+   for (vp = fr_cursor_init(&cursor, head);
+        vp;
+        vp = fr_cursor_next(&cursor) {
+        if (<condition>) {
+            vp = fr_cursor_remove(&cursor);
+            talloc_free(vp);
+        }
+   }
+ @endcode
+ *
  * @param cursor to remove the current pair from.
- * @return NULL on error, else the VALUE_PAIR we just removed.
+ * @return NULL on error, else the VALUE_PAIR that was just removed.
  */
 VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor)
 {
-       VALUE_PAIR *vp, **last;
+       VALUE_PAIR *vp, *before;
 
        if (!fr_assert(cursor->first)) return NULL;     /* cursor must have been initialised */
 
        vp = cursor->current;
-       if (!vp) {
-               return NULL;
-       }
+       if (!vp) return NULL;
 
-       last = cursor->first;
-       while (*last != vp) {
-               last = &(*last)->next;
+       /*
+        *      Where VP is head of the list
+        */
+       if (*(cursor->first) == vp) {
+               *(cursor->first) = vp->next;
+               cursor->current = vp->next;
+               cursor->next = vp->next ? vp->next->next : NULL;
+               goto fixup;
        }
 
-       fr_cursor_next(cursor);   /* Advance the cursor past the one were about to delete */
+       /*
+        *      Where VP is not head of the list
+        */
+       before = *(cursor->first);
+       if (!before) return NULL;
 
-       *last = vp->next;
-       vp->next = NULL;
+       /*
+        *      Find the VP immediately preceding the one being removed
+        */
+       while (before->next != vp) before = before->next;
+
+       cursor->next = before->next = vp->next; /* close the gap */
+       cursor->current = before;               /* current jumps back one, but this is usually desirable */
+
+fixup:
+       vp->next = NULL;                        /* limit scope of fr_pair_list_free() */
 
-       /* Fixup cursor->found if we removed the VP it was referring to */
-       if (vp == cursor->found) cursor->found = *last;
+       /*
+        *      Fixup cursor->found if we removed the VP it was referring to
+        */
+       if (vp == cursor->found) cursor->found = cursor->current;
 
+       /*
+        *      Fixup cursor->last if we removed the VP it was referring to
+        */
+       if (vp == cursor->last) cursor->last = cursor->current;
        return vp;
 }
 
index 2530c76..a56e4ec 100644 (file)
@@ -95,12 +95,9 @@ static fr_fault_cb_t panic_cb = NULL;                        //!< Callback to execute whilst panickin
 
 static bool dump_core;                                 //!< Whether we should drop a core on fatal signals.
 
-static void CC_HINT(format (printf, 1, 2)) _fr_fault_log(char const *msg, ...);
-
-fr_fault_log_t fr_fault_log = _fr_fault_log;           //!< Function to use to process logging output.
 static int fr_fault_log_fd = STDERR_FILENO;            //!< Where to write debug output.
 
-fr_debug_state_t fr_debug_state = DEBUG_STATE_UNKNOWN; //!< Whether were attached to by a debugger.
+fr_debug_state_t fr_debug_state = DEBUG_STATE_UNKNOWN; //!< Whether we're attached to by a debugger.
 
 #ifdef HAVE_SYS_RESOURCE_H
 static struct rlimit core_limits;
@@ -437,7 +434,7 @@ fr_bt_marker_t *fr_backtrace_attach(UNUSED fr_cbuff_t **cbuff, UNUSED TALLOC_CTX
 
 static int _panic_on_free(UNUSED char *foo)
 {
-       fr_fault(SIGUSR1);
+       fr_fault(SIGABRT);
        return -1;      /* this should make the free fail */
 }
 
@@ -622,7 +619,7 @@ static int fr_fault_check_permissions(void)
  *
  * @param sig caught
  */
-void fr_fault(int sig)
+NEVER_RETURNS void fr_fault(int sig)
 {
        char cmd[sizeof(panic_action) + 20];
        char *out = cmd;
@@ -638,11 +635,8 @@ void fr_fault(int sig)
         *      as it may interfere with the operation of the debugger.
         *      If something calls us directly we just raise the signal and let
         *      the debugger handle it how it wants.
-        *
-        *      The only exception are SIGUSR1 and SIGUSR2 which print out various
-        *      debugging info, and should be allowed to continue.
         */
-       if ((fr_debug_state == DEBUG_STATE_ATTACHED) && (sig != SIGUSR1) && (sig != SIGUSR2)) {
+       if (fr_debug_state == DEBUG_STATE_ATTACHED) {
                FR_FAULT_LOG("RAISING SIGNAL: %s", strsignal(sig));
                raise(sig);
                goto finish;
@@ -669,7 +663,7 @@ void fr_fault(int sig)
        if (panic_cb && (panic_cb(sig) < 0)) goto finish;
 
        /*
-        *      Produce a simple backtrace - They've very basic but at least give us an
+        *      Produce a simple backtrace - They're very basic but at least give us an
         *      idea of the area of the code we hit the issue in.
         *
         *      See below in fr_fault_setup() and
@@ -677,30 +671,15 @@ void fr_fault(int sig)
         *      for why we only print backtraces in debug builds if we're using GLIBC.
         */
 #if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
-       {
-               size_t frame_count, i;
+       if (fr_fault_log_fd >= 0) {
+               size_t frame_count;
                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);
-               }
+               backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
        }
 #endif
 
@@ -763,25 +742,51 @@ void fr_fault(int sig)
        FR_FAULT_LOG("Panic action exited with %i", code);
 
 finish:
-#ifdef SIGUSR1
-       if (sig == SIGUSR1) {
-               return;
+       fr_exit_now(1);
+}
+
+/** Callback executed on fatal talloc error
+ *
+ * This is the simple version which mostly behaves the same way as the default
+ * one, and will not call panic_action.
+ *
+ * @param reason string provided by talloc.
+ */
+static void _fr_talloc_fault_simple(char const *reason) CC_HINT(noreturn);
+static void _fr_talloc_fault_simple(char const *reason)
+{
+       FR_FAULT_LOG("talloc abort: %s\n", reason);
+
+#if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
+       if (fr_fault_log_fd >= 0) {
+               size_t frame_count;
+               void *stack[MAX_BT_FRAMES];
+
+               frame_count = backtrace(stack, MAX_BT_FRAMES);
+               FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
+               backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
        }
 #endif
-       fr_exit_now(1);
+       abort();
 }
 
-#ifdef SIGABRT
-/** Work around debuggers which can't backtrace past the signal handler
+/** Callback executed on fatal talloc error
  *
- * At least this provides us some information when we get talloc errors.
+ * Translates a talloc abort into a fr_fault call.
+ * Mostly to work around issues with some debuggers not being able to
+ * attach after a SIGABRT has been raised.
+ *
+ * @param reason string provided by talloc.
  */
+static void _fr_talloc_fault(char const *reason) CC_HINT(noreturn);
 static void _fr_talloc_fault(char const *reason)
 {
-       fr_fault_log("talloc abort: %s\n", reason);
+       FR_FAULT_LOG("talloc abort: %s", reason);
+#ifdef SIGABRT
        fr_fault(SIGABRT);
-}
 #endif
+       fr_exit_now(1);
+}
 
 /** Wrapper to pass talloc log output to our fr_fault_log function
  *
@@ -797,8 +802,9 @@ static void _fr_talloc_log(char const *msg)
  */
 int fr_log_talloc_report(TALLOC_CTX *ctx)
 {
+#define TALLOC_REPORT_MAX_DEPTH 20
+
        FILE *log;
-       int i = 0;
        int fd;
 
        fd = dup(fr_fault_log_fd);
@@ -817,15 +823,24 @@ int fr_log_talloc_report(TALLOC_CTX *ctx)
                fprintf(log, "Current state of talloced memory:\n");
                talloc_report_full(talloc_null_ctx, log);
        } else {
+               int i;
+
                fprintf(log, "Talloc chunk lineage:\n");
                fprintf(log, "%p (%s)", ctx, talloc_get_name(ctx));
-               while ((ctx = talloc_parent(ctx))) fprintf(log, " < %p (%s)", ctx, talloc_get_name(ctx));
+
+               i = 0;
+               while ((i < TALLOC_REPORT_MAX_DEPTH) && (ctx = talloc_parent(ctx))) {
+                       fprintf(log, " < %p (%s)", ctx, talloc_get_name(ctx));
+                       i++;
+               }
                fprintf(log, "\n");
 
+               i = 0;
                do {
                        fprintf(log, "Talloc context level %i:\n", i++);
                        talloc_report_full(ctx, log);
                } while ((ctx = talloc_parent(ctx)) &&
+                        (i < TALLOC_REPORT_MAX_DEPTH) &&
                         (talloc_parent(ctx) != talloc_autofree_ctx) && /* Stop before we hit the autofree ctx */
                         (talloc_parent(ctx) != talloc_null_ctx));      /* Stop before we hit NULL ctx */
        }
@@ -835,16 +850,6 @@ int fr_log_talloc_report(TALLOC_CTX *ctx)
        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", strsignal(sig));
-
-       if (fr_log_talloc_report(NULL) < 0) fr_perror("memreport");
-}
 
 static int _fr_disable_null_tracking(UNUSED bool *p)
 {
@@ -852,6 +857,17 @@ static int _fr_disable_null_tracking(UNUSED bool *p)
        return 0;
 }
 
+/** Register talloc fault handlers
+ *
+ * Just register the fault handlers we need to make talloc
+ * produce useful debugging output.
+ */
+void fr_talloc_fault_setup(void)
+{
+       talloc_set_log_fn(_fr_talloc_log);
+       talloc_set_abort_fn(_fr_talloc_fault_simple);
+}
+
 /** Registers signal handlers to execute panic_action on fatal signal
  *
  * May be called multiple time to change the panic_action/program.
@@ -903,12 +919,6 @@ int fr_fault_setup(char const *cmd, char const *program)
                fr_debug_state_t debug_state;
 
                /*
-                *  Setup the default logger
-                */
-               if (!fr_fault_log) fr_fault_set_log_fn(NULL);
-               talloc_set_log_fn(_fr_talloc_log);
-
-               /*
                 *  Installing signal handlers interferes with some debugging
                 *  operations.  Give the developer control over whether the
                 *  signal handlers are installed or not.
@@ -926,6 +936,8 @@ int fr_fault_setup(char const *cmd, char const *program)
                        debug_state = DEBUG_STATE_ATTACHED;
                }
 
+               talloc_set_log_fn(_fr_talloc_log);
+
                /*
                 *  These signals can't be properly dealt with in the debugger
                 *  if we set our own signal handlers.
@@ -962,13 +974,6 @@ int fr_fault_setup(char const *cmd, char const *program)
                case DEBUG_STATE_ATTACHED:
                        break;
                }
-#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
 
                /*
                 *  Needed for memory reports
@@ -1031,27 +1036,26 @@ void fr_fault_set_cb(fr_fault_cb_t func)
        panic_cb = func;
 }
 
-/** Default logger, logs output to stderr
+/** Log output to the fr_fault_log_fd
  *
+ * We used to support a user defined callback, which was set to a radlog
+ * function. Unfortunately, when logging to syslog, syslog would malloc memory
+ * which would result in a deadlock if fr_fault was triggered from within
+ * a malloc call.
+ *
+ * Now we just write directly to the FD.
  */
-static void CC_HINT(format (printf, 1, 2)) _fr_fault_log(char const *msg, ...)
+void fr_fault_log(char const *msg, ...)
 {
        va_list ap;
 
+       if (fr_fault_log_fd < 0) return;
+
        va_start(ap, msg);
-       vfprintf(stderr, msg, ap);
+       vdprintf(fr_fault_log_fd, 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.
@@ -1061,185 +1065,20 @@ 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(char const *file, int line, VALUE_PAIR const *vp)
-{
-       if (!vp) {
-               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR pointer was NULL", file, line);
-               fr_assert(0);
-               fr_exit_now(1);
-       }
-
-       (void) talloc_get_type_abort(vp, VALUE_PAIR);
-
-       if (!vp->da) {
-               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR da pointer was NULL", file, line);
-               fr_assert(0);
-               fr_exit_now(1);
-       }
-
-       if (vp->data.ptr) switch (vp->da->type) {
-       case PW_TYPE_OCTETS:
-       case PW_TYPE_TLV:
-       {
-               size_t len;
-               TALLOC_CTX *parent;
-
-               if (!talloc_get_type(vp->data.ptr, uint8_t)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
-                                    "uint8_t but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
-                       (void) talloc_get_type_abort(vp->data.ptr, uint8_t);
-               }
-
-               len = talloc_array_length(vp->vp_octets);
-               if (vp->vp_length > len) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
-                                    "uint8_t data buffer length %zu\n", file, line, vp->da->name, vp->vp_length, len);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-
-               parent = talloc_parent(vp->data.ptr);
-               if (parent != vp) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer is not "
-                                    "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
-                                    file, line, vp->da->name,
-                                    vp, parent, parent ? talloc_get_name(parent) : "NULL");
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-       }
-               break;
-
-       case PW_TYPE_STRING:
-       {
-               size_t len;
-               TALLOC_CTX *parent;
-
-               if (!talloc_get_type(vp->data.ptr, char)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
-                                    "char but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
-                       (void) talloc_get_type_abort(vp->data.ptr, char);
-               }
-
-               len = (talloc_array_length(vp->vp_strvalue) - 1);
-               if (vp->vp_length > len) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
-                                    "char buffer length %zu\n", file, line, vp->da->name, vp->vp_length, len);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-
-               if (vp->vp_strvalue[vp->vp_length] != '\0') {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer not \\0 "
-                                    "terminated\n", file, line, vp->da->name);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-
-               parent = talloc_parent(vp->data.ptr);
-               if (parent != vp) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer is not "
-                                    "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
-                                    file, line, vp->da->name,
-                                    vp, parent, parent ? talloc_get_name(parent) : "NULL");
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-       }
-               break;
-
-       default:
-               break;
-       }
-
-       if (vp->da->flags.is_unknown) {
-               (void) talloc_get_type_abort(vp->da, DICT_ATTR);
-       } else {
-               DICT_ATTR const *da;
-
-               /*
-                *      Attribute may be present with multiple names
-                */
-               da = dict_attrbyname(vp->da->name);
-               if (!da) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR attribute %p \"%s\" (%s) "
-                                    "not found in global dictionary",
-                                    file, line, vp->da, vp->da->name,
-                                    fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-
-               if (da->type == PW_TYPE_COMBO_IP_ADDR) {
-                       da = dict_attrbytype(vp->da->attr, vp->da->vendor, vp->da->type);
-                       if (!da) {
-                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR attribute %p \"%s\" "
-                                            "variant (%s) not found in global dictionary",
-                                            file, line, vp->da, vp->da->name,
-                                            fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
-                               fr_assert(0);
-                               fr_exit_now(1);
-                       }
-               }
-
-
-               if (da != vp->da) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR "
-                                    "dictionary pointer %p \"%s\" (%s) "
-                                    "and global dictionary pointer %p \"%s\" (%s) differ",
-                                    file, line, vp->da, vp->da->name,
-                                    fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"),
-                                    da, da->name, fr_int2str(dict_attr_types, da->type, "<INVALID>"));
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-       }
-}
-
-/*
- *     Verify a pair list
+/** A soft assertion which triggers the fault handler in debug builds
+ *
+ * @param file the assertion failed in.
+ * @param line of the assertion in the file.
+ * @param expr that was evaluated.
+ * @param cond Result of evaluating the expression.
+ * @return the value of cond.
  */
-void fr_verify_list(char const *file, int line, 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_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: Expected VALUE_PAIR \"%s\" to be parented "
-                                    "by %p (%s), instead parented by %p (%s)\n",
-                                    file, line, 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);
-
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-
-       }
-}
-#endif
-
 bool fr_assert_cond(char const *file, int line, char const *expr, bool cond)
 {
        if (!cond) {
                FR_FAULT_LOG("SOFT ASSERT FAILED %s[%u]: %s", file, line, expr);
-#if !defined(NDEBUG) && defined(SIGUSR1)
-               fr_fault(SIGUSR1);
+#if !defined(NDEBUG)
+               fr_fault(SIGABRT);
 #endif
                return false;
        }
@@ -1249,8 +1088,7 @@ bool fr_assert_cond(char const *file, int line, char const *expr, bool cond)
 
 /** Exit possibly printing a message about why we're exiting.
  *
- * Use the fr_exit(status) macro instead of calling this function
- * directly.
+ * @note Use the fr_exit(status) macro instead of calling this function directly.
  *
  * @param file where fr_exit() was called.
  * @param line where fr_exit() was called.
@@ -1274,8 +1112,7 @@ void NEVER_RETURNS _fr_exit(char const *file, int line, int status)
 
 /** Exit possibly printing a message about why we're exiting.
  *
- * Use the fr_exit_now(status) macro instead of calling this function
- * directly.
+ * @note Use the fr_exit_now(status) macro instead of calling this function directly.
  *
  * @param file where fr_exit_now() was called.
  * @param line where fr_exit_now() was called.
index 697322c..03de2b3 100644 (file)
@@ -5,8 +5,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -163,6 +162,10 @@ int const fr_attr_shift[MAX_TLV_NEST + 1] = { 0, 8, 16, 24, 29 };
 
 int const fr_attr_mask[MAX_TLV_NEST + 1] = { 0xff, 0xff, 0xff, 0x1f, 0x07 };
 
+/*
+ *     attr & fr_attr_parent_mask[i] == Nth parent of attr
+ */
+static unsigned int const fr_attr_parent_mask[MAX_TLV_NEST + 1] = { 0, 0x000000ff, 0x0000ffff, 0x00ffffff, 0x1fffffff };
 
 /*
  *     Create the hash of the name.
@@ -636,6 +639,73 @@ int dict_valid_name(char const *name)
        return 0;
 }
 
+
+/*
+ *     Bamboo skewers under the fingernails in 5, 4, 3, 2, ...
+ */
+static DICT_ATTR const *dict_parent(unsigned int attr, unsigned int vendor)
+{
+       int i;
+       unsigned int base_vendor;
+
+       /*
+        *      RFC attributes can't be of type "tlv".
+        */
+       if (!vendor) return NULL;
+
+       base_vendor = vendor & (FR_MAX_VENDOR - 1);
+
+       /*
+        *      It's a real vendor.
+        */
+       if (base_vendor != 0) {
+               DICT_VENDOR const *dv;
+
+               dv = dict_vendorbyvalue(base_vendor);
+               if (!dv) return NULL;
+
+               /*
+                *      Only standard format attributes can be of type "tlv",
+                *      Except for DHCP.  <sigh>
+                */
+               if ((vendor != 54) && ((dv->type != 1) || (dv->length != 1))) return NULL;
+
+               for (i = MAX_TLV_NEST; i > 0; i--) {
+                       unsigned int parent;
+
+                       parent = attr & fr_attr_parent_mask[i];
+
+                       if (parent != attr) return dict_attrbyvalue(parent, vendor); /* not base_vendor */
+               }
+
+               /*
+                *      It was a top-level VSA.  There's no parent.
+                *      We COULD return the appropriate enclosing VSA
+                *      (26, or 241.26, etc.) but that's not what we
+                *      want.
+                */
+               return NULL;
+       }
+
+       /*
+        *      It's an extended attribute.  Return the base Extended-Attr-X
+        */
+       if (attr < 256) return dict_attrbyvalue((vendor / FR_MAX_VENDOR) & 0xff, 0);
+
+       /*
+        *      Figure out which attribute it is.
+        */
+       for (i = MAX_TLV_NEST; i > 0; i--) {
+               unsigned int parent;
+
+               parent = attr & fr_attr_parent_mask[i];
+               if (parent != attr) return dict_attrbyvalue(parent, vendor); /* not base_vendor */
+       }
+
+       return NULL;
+}
+
+
 /** Add an attribute to the dictionary
  *
  * @return 0 on success -1 on failure.
@@ -644,9 +714,9 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type,
                 ATTR_FLAGS flags)
 {
        size_t namelen;
-       static int      max_attr = 0;
-       DICT_ATTR const *da;
+       DICT_ATTR const *parent;
        DICT_ATTR *n;
+       static int      max_attr = 0;
 
        namelen = strlen(name);
        if (namelen >= DICT_ATTR_MAX_NAME_LEN) {
@@ -693,6 +763,61 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type,
        }
 
        /*
+        *      Check the parent attribute, and set the various flags
+        *      based on the parents values.  It's OK for the caller
+        *      to not set them, as we'll set them.  But if the caller
+        *      sets them when he's not supposed to set them, that's
+        *      an error.
+        */
+       parent = dict_parent(attr, vendor);
+       if (parent) {
+               /*
+                *      We're still in the same space and the parent isn't a TLV.  That's an error.
+                *
+                *      Otherwise, dict_parent() has taken us from an Extended sub-attribute to
+                *      a *the* Extended attribute, whish isn't what we want here.
+                */
+               if ((vendor == parent->vendor) && (parent->type != PW_TYPE_TLV)) {
+                       fr_strerror_printf("dict_addattr: Attribute %s has parent attribute %s which is not of type 'tlv'",
+                                          name, parent->name);
+                       return -1;
+               }
+
+               flags.extended |= parent->flags.extended;
+               flags.long_extended |= parent->flags.long_extended;
+               flags.evs |= parent->flags.evs;
+       }
+
+       /*
+        *      Manually extended flags for extended attributes.  We
+        *      can't expect the caller to know all of the details of the flags.
+        */
+       if (vendor >= FR_MAX_VENDOR) {
+               DICT_ATTR const *da;
+
+               /*
+                *      Trying to manually create an extended
+                *      attribute, but the parent extended attribute
+                *      doesn't exist?  That's an error.
+                */
+               da = dict_attrbyvalue(vendor / FR_MAX_VENDOR, 0);
+               if (!da) {
+                       fr_strerror_printf("Extended attributes must be defined from the extended space");
+                       return -1;
+               }
+
+               flags.extended |= da->flags.extended;
+               flags.long_extended |= da->flags.long_extended;
+               flags.evs |= da->flags.evs;
+
+               /*
+                *      There's still a real vendor.  Since it's an
+                *      extended attribute, set the EVS flag.
+                */
+               if ((vendor & (FR_MAX_VENDOR -1)) != 0) flags.evs = 1;
+       }
+
+       /*
         *      Additional checks for extended attributes.
         */
        if (flags.extended || flags.long_extended || flags.evs) {
@@ -715,28 +840,11 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type,
                        fr_strerror_printf("dict_addattr: Attributes of type \"evs\" MUST have a parent of type \"extended\"");
                        return -1;
                }
-
-               /* VSAs cannot be of format EVS */
-               if ((vendor & (FR_MAX_VENDOR - 1)) != 0) {
-                       fr_strerror_printf("dict_addattr: Attribute of type \"evs\" fails internal sanity check");
-                       return -1;
-               }
        }
 
        /*
-        *      Allow for generic pointers
+        *      Do various sanity checks.
         */
-       switch (type) {
-       default:
-               break;
-
-       case PW_TYPE_STRING:
-       case PW_TYPE_OCTETS:
-       case PW_TYPE_TLV:
-               flags.is_pointer = true;
-               break;
-       }
-
        if (attr < 0) {
                fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (less than zero)");
                return -1;
@@ -764,6 +872,118 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type,
                return -1;
        }
 
+       if (flags.length && (type != PW_TYPE_OCTETS)) {
+               fr_strerror_printf("The \"length\" flag can only be set for attributes of type \"octets\"");
+               return -1;
+       }
+
+       if (flags.length && (flags.has_tag || flags.array || flags.is_tlv || flags.has_tlv ||
+                            flags.concat || flags.evs || flags.extended || flags.long_extended ||
+                            (flags.encrypt > FLAG_ENCRYPT_USER_PASSWORD))) {
+               fr_strerror_printf("The \"length\" flag cannot be used with any other flag");
+               return -1;
+       }
+
+       /*
+        *      Force "length" for data types of fixed length;
+        */
+       switch (type) {
+       case PW_TYPE_BYTE:
+               flags.length = 1;
+               break;
+
+       case PW_TYPE_SHORT:
+               flags.length = 2;
+               break;
+
+       case PW_TYPE_DATE:
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_INTEGER:
+       case PW_TYPE_SIGNED:
+               flags.length = 4;
+               break;
+
+       case PW_TYPE_INTEGER64:
+               flags.length = 8;
+               break;
+
+       case PW_TYPE_ETHERNET:
+               flags.length = 6;
+               break;
+
+       case PW_TYPE_IFID:
+               flags.length = 8;
+               break;
+
+       case PW_TYPE_IPV6_ADDR:
+               flags.length = 16;
+               break;
+
+       case PW_TYPE_EXTENDED:
+               if ((vendor != 0) || (attr < 241)) {
+                       fr_strerror_printf("Attributes of type \"extended\" MUST be "
+                                          "RFC attributes with value >= 241.");
+                       return -1;
+               }
+
+               flags.length = 0;
+               flags.extended = 1;
+               break;
+
+       case PW_TYPE_LONG_EXTENDED:
+               if ((vendor != 0) || (attr < 241)) {
+                       fr_strerror_printf("Attributes of type \"long-extended\" MUST "
+                                          "be RFC attributes with value >= 241.");
+                       return -1;
+               }
+
+               flags.length = 0;
+               flags.extended = 1;
+               flags.long_extended = 1;
+               break;
+
+       case PW_TYPE_EVS:
+               if (attr != PW_VENDOR_SPECIFIC) {
+                       fr_strerror_printf("Attributes of type \"evs\" MUST have "
+                                          "attribute code 26.");
+                       return -1;
+               }
+
+               flags.length = 0;
+               flags.extended = 1;
+               flags.evs = 1;
+               break;
+
+       case PW_TYPE_STRING:
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_TLV:
+               flags.is_pointer = true;
+               break;
+
+       default:
+               break;
+       }
+
+       /*
+        *      Stupid hacks for MS-CHAP-MPPE-Keys.  The User-Password
+        *      encryption method has no provisions for encoding the
+        *      length of the data.  For User-Password, the data is
+        *      (presumably) all printable non-zero data.  For
+        *      MS-CHAP-MPPE-Keys, the data is binary crap.  So... we
+        *      MUST specify a length in the dictionary.
+        */
+       if ((flags.encrypt == FLAG_ENCRYPT_USER_PASSWORD) && (type != PW_TYPE_STRING)) {
+               if (type != PW_TYPE_OCTETS) {
+                       fr_strerror_printf("The \"encrypt=1\" flag cannot be used with non-string data types");
+                       return -1;
+               }
+
+               if (flags.length == 0) {
+                       fr_strerror_printf("The \"encrypt=1\" flag MUST be used with an explicit length for 'octets' data types");
+                       return -1;
+               }
+       }
+
        if ((vendor & (FR_MAX_VENDOR -1)) != 0) {
                DICT_VENDOR *dv;
                static DICT_VENDOR *last_vendor = NULL;
@@ -825,49 +1045,6 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type,
                } /* else 256..65535 are allowed */
 
                /*
-                *      If the attribute is in the standard space, AND
-                *      has a sub-type (e.g. 241.1 or 255.3), then its
-                *      number is placed into the upper 8 bits of the
-                *      vendor field.
-                *
-                *      This also happens for the new VSAs.
-                *
-                *      If we find it, then set the various flags
-                *      based on what we see.
-                */
-               if (vendor >= FR_MAX_VENDOR) {
-                       unsigned int parent;
-
-                       parent = (vendor / FR_MAX_VENDOR) & 0xff;
-
-                       da = dict_attrbyvalue(parent, 0);
-                       if (!da) {
-                               fr_strerror_printf("dict_addattr: ATTRIBUTE refers to unknown parent attribute %u.", parent);
-                               return -1;
-                       }
-
-                       /*
-                        *      These flags are inherited from the
-                        *      parent.
-                        */
-                       flags.extended = da->flags.extended;
-                       flags.long_extended = da->flags.long_extended;
-
-                       /*
-                        *      Non-extended attributes can't have VSAs.
-                        */
-                       if (!flags.extended &&
-                           ((vendor & (FR_MAX_VENDOR - 1)) != 0)) {
-                               fr_strerror_printf("dict_addattr: ATTRIBUTE cannot be a VSA");
-                               return -1;
-                       }
-
-                       if ((vendor & (FR_MAX_VENDOR - 1)) != 0) {
-                               flags.evs = 1;
-                       }
-               }
-
-               /*
                 *      <sigh> Alvarion, being *again* a horribly
                 *      broken vendor, has re-used the WiMAX format in
                 *      their proprietary vendor space.  This re-use
@@ -875,7 +1052,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type,
                 *      Alvarion dictionaries.
                 */
                flags.wimax = dv->flags;
-       }
+       } /* it's a VSA of some kind */
 
        /*
         *      Create a new attribute for the list
@@ -1294,22 +1471,6 @@ int dict_str2oid(char const *ptr, unsigned int *pvalue, unsigned int *pvendor,
        return tlv_depth;
 }
 
-/*
- *     Bamboo skewers under the fingernails in 5, 4, 3, 2, ...
- */
-static DICT_ATTR const *dict_parent(unsigned int attr, unsigned int vendor)
-{
-       if (vendor < FR_MAX_VENDOR) {
-               return dict_attrbyvalue(attr & 0xff, vendor);
-       }
-
-       if (attr < 256) {
-               return dict_attrbyvalue((vendor / FR_MAX_VENDOR) & 0xff, 0);
-       }
-
-       return dict_attrbyvalue(attr & 0xff, vendor);
-}
-
 
 /*
  *     Process the ATTRIBUTE command
@@ -1323,7 +1484,7 @@ static int process_attribute(char const* fn, int const line,
        unsigned int    vendor = 0;
        unsigned int    value;
        int             type;
-       unsigned int    length = 0;
+       unsigned int    length;
        ATTR_FLAGS      flags;
        char            *p;
 
@@ -1422,84 +1583,14 @@ static int process_attribute(char const* fn, int const line,
                        fr_strerror_printf("dict_init: %s[%d]: invalid length", fn, line);
                        return -1;
                }
+
+               flags.length = length;
        }
 
        /*
-        *      Only look up the vendor if the string
-        *      is non-empty.
+        *      Parse options.
         */
-       if (argc < 4) {
-               /*
-                *      Force "length" for data types of fixed length;
-                */
-               switch (type) {
-               case PW_TYPE_BYTE:
-                       length = 1;
-                       break;
-
-               case PW_TYPE_SHORT:
-                       length = 2;
-                       break;
-
-               case PW_TYPE_DATE:
-               case PW_TYPE_IPV4_ADDR:
-               case PW_TYPE_INTEGER:
-               case PW_TYPE_SIGNED:
-                       length = 4;
-                       break;
-
-               case PW_TYPE_INTEGER64:
-                       length = 8;
-                       break;
-
-               case PW_TYPE_ETHERNET:
-                       length = 6;
-                       break;
-
-               case PW_TYPE_IFID:
-                       length = 8;
-                       break;
-
-               case PW_TYPE_IPV6_ADDR:
-                       length = 16;
-                       break;
-
-               case PW_TYPE_EXTENDED:
-                       if ((vendor != 0) || (value < 241)) {
-                               fr_strerror_printf("dict_init: %s[%d]: Attributes of type \"extended\" MUST be "
-                                                  "RFC attributes with value >= 241.", fn, line);
-                               return -1;
-                       }
-                       flags.extended = 1;
-                       break;
-
-               case PW_TYPE_LONG_EXTENDED:
-                       if ((vendor != 0) || (value < 241)) {
-                               fr_strerror_printf("dict_init: %s[%d]: Attributes of type \"long-extended\" MUST "
-                                                  "be RFC attributes with value >= 241.", fn, line);
-                               return -1;
-                       }
-                       flags.extended = 1;
-                       flags.long_extended = 1;
-                       break;
-
-               case PW_TYPE_EVS:
-                       flags.extended = 1;
-                       flags.evs = 1;
-                       if (value != PW_VENDOR_SPECIFIC) {
-                               fr_strerror_printf("dict_init: %s[%d]: Attributes of type \"evs\" MUST have "
-                                                  "attribute code 26.", fn, line);
-                               return -1;
-                       }
-                       break;
-
-               default:
-                       break;
-               }
-
-               flags.length = length;
-
-       } else {                /* argc == 4: we have options */
+       if (argc >= 4) {
                char *key, *next, *last;
 
                /*
@@ -1510,11 +1601,6 @@ static int process_attribute(char const* fn, int const line,
                        return -1;
                }
 
-               if (length != 0) {
-                       fr_strerror_printf("dict_init: %s[%d]: length cannot be used with options", fn, line);
-                       return -1;
-               }
-
                key = argv[3];
                do {
                        next = strchr(key, ',');
@@ -1678,7 +1764,7 @@ static int process_attribute(char const* fn, int const line,
                }
 
                /*
-                *
+                *      Shift the value left.
                 */
                value <<= fr_attr_shift[tlv_depth];
                value |= block_tlv->attr;
@@ -1827,6 +1913,74 @@ static int process_value_alias(char const* fn, int const line, char **argv,
 }
 
 
+static int parse_format(char const *fn, int line, char const *format, int *pvalue, int *ptype, int *plength, bool *pcontinuation)
+{
+       char const *p;
+       int type, length;
+       bool continuation = false;
+
+       if (strncasecmp(format, "format=", 7) != 0) {
+               fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected \"format=\", got \"%s\"",
+                                  fn, line, format);
+               return -1;
+       }
+
+       p = format + 7;
+       if ((strlen(p) < 3) ||
+           !isdigit((int) p[0]) ||
+           (p[1] != ',') ||
+           !isdigit((int) p[2]) ||
+           (p[3] && (p[3] != ','))) {
+               fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
+                                  fn, line, p);
+               return -1;
+       }
+
+       type = (int) (p[0] - '0');
+       length = (int) (p[2] - '0');
+
+       if ((type != 1) && (type != 2) && (type != 4)) {
+               fr_strerror_printf("dict_init: %s[%d]: invalid type value %d for VENDOR",
+                                  fn, line, type);
+               return -1;
+       }
+
+       if ((length != 0) && (length != 1) && (length != 2)) {
+               fr_strerror_printf("dict_init: %s[%d]: invalid length value %d for VENDOR",
+                                  fn, line, length);
+               return -1;
+       }
+
+       if (p[3] == ',') {
+               if (!p[4]) {
+                       fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
+                                          fn, line, p);
+                       return -1;
+               }
+
+               if ((p[4] != 'c') ||
+                   (p[5] != '\0')) {
+                       fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
+                                          fn, line, p);
+                       return -1;
+               }
+               continuation = true;
+
+               if ((*pvalue != VENDORPEC_WIMAX) ||
+                   (type != 1) || (length != 1)) {
+                       fr_strerror_printf("dict_init: %s[%d]: Only WiMAX VSAs can have continuations",
+                                          fn, line);
+                       return -1;
+               }
+       }
+
+       *ptype = type;
+       *plength = length;
+       *pcontinuation = continuation;
+       return 0;
+}
+
+
 /*
  *     Process the VENDOR command
  */
@@ -1834,8 +1988,9 @@ static int process_vendor(char const* fn, int const line, char **argv,
                          int argc)
 {
        int             value;
+       int             type, length;
        bool            continuation = false;
-       char const      *format = NULL;
+       DICT_VENDOR     *dv;
 
        if ((argc < 2) || (argc > 3)) {
                fr_strerror_printf( "dict_init: %s[%d] invalid VENDOR entry",
@@ -1865,94 +2020,40 @@ static int process_vendor(char const* fn, int const line, char **argv,
        }
 
        /*
-        *      Look for a format statement
+        *      Look for a format statement.  Allow it to over-ride the hard-coded formats below.
         */
        if (argc == 3) {
-               format = argv[2];
+               if (parse_format(fn, line, argv[2], &value, &type, &length, &continuation) < 0) {
+                       return -1;
+               }
 
        } else if (value == VENDORPEC_USR) { /* catch dictionary screw-ups */
-               format = "format=4,0";
+               type = 4;
+               length = 0;
 
        } else if (value == VENDORPEC_LUCENT) {
-               format = "format=2,1";
+               type = 2;
+               length = 1;
 
        } else if (value == VENDORPEC_STARENT) {
-               format = "format=2,2";
-
-       } /* else no fixups to do */
-
-       if (format) {
-               int type, length;
-               char const *p;
-               DICT_VENDOR *dv;
+               type = 2;
+               length = 2;
 
-               if (strncasecmp(format, "format=", 7) != 0) {
-                       fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected \"format=\", got \"%s\"",
-                                  fn, line, format);
-                       return -1;
-               }
-
-               p = format + 7;
-               if ((strlen(p) < 3) ||
-                   !isdigit((int) p[0]) ||
-                   (p[1] != ',') ||
-                   !isdigit((int) p[2]) ||
-                   (p[3] && (p[3] != ','))) {
-                       fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
-                                  fn, line, p);
-                       return -1;
-               }
-
-               type = (int) (p[0] - '0');
-               length = (int) (p[2] - '0');
-
-               if (p[3] == ',') {
-                       if (!p[4]) {
-                               fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
-                                  fn, line, p);
-                               return -1;
-                       }
-
-                       if ((p[4] != 'c') ||
-                           (p[5] != '\0')) {
-                               fr_strerror_printf("dict_init: %s[%d]: Invalid format for VENDOR.  Expected text like \"1,1\", got \"%s\"",
-                                          fn, line, p);
-                               return -1;
-                       }
-                       continuation = true;
-
-                       if ((value != VENDORPEC_WIMAX) ||
-                           (type != 1) || (length != 1)) {
-                               fr_strerror_printf("dict_init: %s[%d]: Only WiMAX VSAs can have continuations",
-                                          fn, line);
-                               return -1;
-                       }
-               }
+       } else {
+               type = length = 1;
+       }
 
-               dv = dict_vendorbyvalue(value);
-               if (!dv) {
-                       fr_strerror_printf("dict_init: %s[%d]: Failed adding format for VENDOR",
+       dv = dict_vendorbyvalue(value);
+       if (!dv) {
+               fr_strerror_printf("dict_init: %s[%d]: Failed adding format for VENDOR",
                                   fn, line);
-                       return -1;
-               }
-
-               if ((type != 1) && (type != 2) && (type != 4)) {
-                       fr_strerror_printf("dict_init: %s[%d]: invalid type value %d for VENDOR",
-                                  fn, line, type);
-                       return -1;
-               }
-
-               if ((length != 0) && (length != 1) && (length != 2)) {
-                       fr_strerror_printf("dict_init: %s[%d]: invalid length value %d for VENDOR",
-                                  fn, line, length);
-                       return -1;
-               }
-
-               dv->type = type;
-               dv->length = length;
-               dv->flags = continuation;
+               return -1;
        }
 
+       dv->type = type;
+       dv->length = length;
+       dv->flags = continuation;
+
        return 0;
 }
 
@@ -2355,7 +2456,7 @@ static int my_dict_init(char const *parent, char const *filename,
                                 *      attribute into the upper 8
                                 *      bits of the vendor ID
                                 */
-                               block_vendor |= (da->attr & fr_attr_mask[0]) * FR_MAX_VENDOR;
+                               block_vendor |= da->vendor;
                        }
 
                        continue;
index 0e44a95..fa1e0aa 100644 (file)
@@ -6,8 +6,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -227,9 +226,28 @@ int fr_event_insert(fr_event_list_t *el, fr_event_callback_t callback, void *ctx
                return 0;
        }
 
-       if (*parent) fr_event_delete(el, parent);
+       /*
+        *      If there is an event, re-use it instead of freeing it
+        *      and allocating a new one.
+        */
+       if (*parent) {
+               int ret;
+
+#ifndef NDEBUG
+               ev = talloc_get_type_abort(*parent, fr_event_t);
+#else
+               ev = *parent;
+#endif
+
+               ret = fr_heap_extract(el->times, ev);
+               fr_assert(ret == 1);    /* events MUST be in the heap */
+
+               memset(ev, 0, sizeof(*ev));
+       } else {
+               ev = talloc_zero(el, fr_event_t);
+               if (!ev) return 0;
+       }
 
-       ev = talloc_zero(el, fr_event_t);
        ev->callback = callback;
        ev->ctx = ctx;
        ev->when = *when;
@@ -286,7 +304,7 @@ int fr_event_run(fr_event_list_t *el, struct timeval *when)
        /*
         *      Delete the event before calling it.
         */
-       fr_event_delete(el, &ev);
+       fr_event_delete(el, ev->parent);
 
        callback(ctx);
        return 1;
index fdea2c6..7a9ecfa 100644 (file)
@@ -6,8 +6,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -28,30 +27,26 @@ RCSID("$Id$")
 #include <freeradius-devel/libradius.h>
 
 struct fr_fifo_t {
-       int num;
-       int first, last;
-       int max;
+       unsigned int num;
+       unsigned int first, last;
+       unsigned int max;
        fr_fifo_free_t freeNode;
 
        void *data[1];
 };
 
 
-fr_fifo_t *fr_fifo_create(int max, fr_fifo_free_t freeNode)
+fr_fifo_t *fr_fifo_create(TALLOC_CTX *ctx, int max, fr_fifo_free_t freeNode)
 {
        fr_fifo_t *fi;
 
        if ((max < 2) || (max > (1024 * 1024))) return NULL;
 
-       fi = malloc(sizeof(*fi) + (sizeof(fi->data[0])*max));
+       fi = talloc_zero_size(ctx, (sizeof(*fi) + (sizeof(fi->data[0])*max)));
        if (!fi) return NULL;
-
-       memset(fi, 0, sizeof(*fi));
+       talloc_set_type(fi, fr_fifo_t);
 
        fi->max = max;
-       fi->first = 0;
-       fi->last = 0;
-       fi->num = 0;
        fi->freeNode = freeNode;
 
        return fi;
@@ -59,13 +54,13 @@ fr_fifo_t *fr_fifo_create(int max, fr_fifo_free_t freeNode)
 
 void fr_fifo_free(fr_fifo_t *fi)
 {
-       int i;
+       unsigned int i;
 
        if (!fi) return;
 
        if (fi->freeNode) {
                for (i = 0 ; i < fi->num; i++) {
-                       int element;
+                       unsigned int element;
 
                        element = i + fi->first;
                        if (element > fi->max) {
@@ -78,7 +73,7 @@ void fr_fifo_free(fr_fifo_t *fi)
        }
 
        memset(fi, 0, sizeof(*fi));
-       free(fi);
+       talloc_free(fi);
 }
 
 int fr_fifo_push(fr_fifo_t *fi, void *data)
@@ -117,7 +112,7 @@ void *fr_fifo_peek(fr_fifo_t *fi)
        return fi->data[fi->first];
 }
 
-int fr_fifo_num_elements(fr_fifo_t *fi)
+unsigned int fr_fifo_num_elements(fr_fifo_t *fi)
 {
        if (!fi) return 0;
 
@@ -138,7 +133,7 @@ int main(int argc, char **argv)
        int i, j, array[MAX];
        fr_fifo_t *fi;
 
-       fi = fr_fifo_create(MAX, NULL);
+       fi = fr_fifo_create(NULL, MAX, NULL);
        if (!fi) fr_exit(1);
 
        for (j = 0; j < 5; j++) {
index defe3c2..4868cd3 100644 (file)
@@ -5,8 +5,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -968,8 +967,7 @@ int ascend_parse_filter(value_data_t *out, char const *value, size_t len)
         *      Once the filter is *completely* parsed, then we will
         *      over-write it with the final binary filter.
         */
-       p = talloc_memdup(NULL, value, len+1);
-       p[len] = '\0';
+       p = talloc_bstrndup(NULL, value, len);
 
        /*
         *      Rather than printing specific error messages, we create
@@ -1064,7 +1062,6 @@ int ascend_parse_filter(value_data_t *out, char const *value, size_t len)
         */
        if (rcode == 0) memcpy(out->filter, &filter, sizeof(filter));
        talloc_free(p);
-       printf("%i", rcode);
 
        return rcode;
 }
index aca0be4..2022362 100644 (file)
@@ -9,31 +9,31 @@
 
 RCSID("$Id$")
 
-#include       <freeradius-devel/libradius.h>
+#include <freeradius-devel/libradius.h>
 
-#include       <ctype.h>
-#include       <sys/param.h>
+#include <ctype.h>
+#include <sys/param.h>
 
 #ifndef HAVE_GETNAMEINFO
-#undef LOCAL_GETHOSTBYNAMERSTYLE
-#ifndef GETHOSTBYNAMERSTYLE
-#define LOCAL_GETHOSTBYNAMERSTYLE 1
+#  undef LOCAL_GETHOSTBYNAMERSTYLE
+#  ifndef GETHOSTBYNAMERSTYLE
+#    define LOCAL_GETHOSTBYNAMERSTYLE 1
 #elif (GETHOSTBYNAMERSTYLE != SYSVSTYLE) && (GETHOSTBYNAMERSTYLE != GNUSTYLE)
-#define LOCAL_GETHOSTBYNAMERSTYLE 1
-#endif /* GETHOSTBYNAMERSTYLE */
+#    define LOCAL_GETHOSTBYNAMERSTYLE 1
+#  endif /* GETHOSTBYNAMERSTYLE */
 #endif
 
 #ifndef HAVE_GETADDRINFO
-#undef LOCAL_GETHOSTBYADDRR
-#ifndef GETHOSTBYADDRRSTYLE
-#define LOCAL_GETHOSTBYADDRR 1
-#elif (GETHOSTBYADDRRSTYLE != SYSVSTYLE) && (GETHOSTBYADDRRSTYLE != GNUSTYLE)
-#define LOCAL_GETHOSTBYADDRR 1
-#endif /* GETHOSTBYADDRRSTYLE */
+#  undef LOCAL_GETHOSTBYADDRR
+#  ifndef GETHOSTBYADDRRSTYLE
+#    define LOCAL_GETHOSTBYADDRR 1
+#  elif (GETHOSTBYADDRRSTYLE != SYSVSTYLE) && (GETHOSTBYADDRRSTYLE != GNUSTYLE)
+#    define LOCAL_GETHOSTBYADDRR 1
+#  endif /* GETHOSTBYADDRRSTYLE */
 #endif
 
 #ifdef HAVE_PTHREAD_H
-#include       <pthread.h>
+#  include <pthread.h>
 
 /* Thread safe DNS lookups */
 /*
@@ -42,15 +42,15 @@ RCSID("$Id$")
  *     that is the case then use only one mutex instead of separate
  *     mutexes
  */
-#ifdef LOCAL_GETHOSTBYNAMERSTYLE
+#  ifdef LOCAL_GETHOSTBYNAMERSTYLE
 static int fr_hostbyname = 0;
 static pthread_mutex_t fr_hostbyname_mutex;
-#endif
+#  endif
 
-#ifdef LOCAL_GETHOSTBYNAMERSTYLE
+#  ifdef LOCAL_GETHOSTBYNAMERSTYLE
 static int fr_hostbyaddr = 0;
 static pthread_mutex_t fr_hostbyaddr_mutex;
-#endif
+#  endif
 
 #endif
 
@@ -74,9 +74,8 @@ static pthread_mutex_t fr_hostbyaddr_mutex;
  *  ---------------------------------------------------------------
  */
 #if defined(LOCAL_GETHOSTBYNAMER) || defined(LOCAL_GETHOSTBYADDRR)
-#define BUFFER_OVERFLOW 255
-static int copy_hostent(struct hostent *from, struct hostent *to,
-                       char *buffer, int buflen, int *error)
+#  define BUFFER_OVERFLOW 255
+static int copy_hostent(struct hostent *from, struct hostent *to, char *buffer, int buflen, int *error)
 {
        int i, len;
        char *ptr = buffer;
@@ -87,43 +86,44 @@ static int copy_hostent(struct hostent *from, struct hostent *to,
        to->h_name = (char *)ptr;
 
        /* copy hostname to buffer */
-       len=strlen(from->h_name)+1;
+       len = strlen(from->h_name) + 1;
        strcpy(ptr, from->h_name);
        ptr += len;
 
        /* copy aliases to buffer */
        to->h_aliases = (char**)ptr;
-       for(i = 0; from->h_aliases[i]; i++);
+       for (i = 0; from->h_aliases[i]; i++);
        ptr += (i+1) * sizeof(char *);
 
-       for(i = 0; from->h_aliases[i]; i++) {
-          len = strlen(from->h_aliases[i])+1;
-          if ((ptr-buffer)+len < buflen) {
-          to->h_aliases[i] = ptr;
-                  strcpy(ptr, from->h_aliases[i]);
-          ptr += len;
-          } else {
-          *error = BUFFER_OVERFLOW;
-          return *error;
-          }
+       for (i = 0; from->h_aliases[i]; i++) {
+               len = strlen(from->h_aliases[i])+1;
+               if ((ptr-buffer) + len < buflen) {
+                       to->h_aliases[i] = ptr;
+                       strcpy(ptr, from->h_aliases[i]);
+                       ptr += len;
+               } else {
+                       *error = BUFFER_OVERFLOW;
+                       return *error;
+               }
        }
        to->h_aliases[i] = NULL;
 
        /* copy addr_list to buffer */
        to->h_addr_list = (char**)ptr;
-       for(i = 0; (int *)from->h_addr_list[i] != 0; i++);
-       ptr += (i+1) * sizeof(int *);
-
-       for(i = 0; (int *)from->h_addr_list[i] != 0; i++) {
-          len = sizeof(int);
-          if ((ptr-buffer)+len < buflen) {
-          to->h_addr_list[i] = ptr;
-          memcpy(ptr, from->h_addr_list[i], len);
-          ptr += len;
-          } else {
-          *error = BUFFER_OVERFLOW;
-               return *error;
-          }
+       for (i = 0; (int *)from->h_addr_list[i] != 0; i++);
+       ptr += (i + 1) * sizeof(int *);
+
+       for (i = 0; (int *)from->h_addr_list[i] != 0; i++) {
+               len = sizeof(int);
+
+               if ((ptr-buffer)+len < buflen) {
+                       to->h_addr_list[i] = ptr;
+                       memcpy(ptr, from->h_addr_list[i], len);
+                       ptr += len;
+               } else {
+                       *error = BUFFER_OVERFLOW;
+                       return *error;
+               }
        }
        to->h_addr_list[i] = 0;
        return *error;
@@ -137,26 +137,26 @@ gethostbyname_r(char const *hostname, struct hostent *result,
 {
        struct hostent *hp;
 
-#ifdef HAVE_PTHREAD_H
+#  ifdef HAVE_PTHREAD_H
        if (fr_hostbyname == 0) {
-       pthread_mutex_init(&fr_hostbyname_mutex, NULL);
-       fr_hostbyname = 1;
+               pthread_mutex_init(&fr_hostbyname_mutex, NULL);
+               fr_hostbyname = 1;
        }
        pthread_mutex_lock(&fr_hostbyname_mutex);
-#endif
+#  endif
 
        hp = gethostbyname(hostname);
        if ((!hp) || (hp->h_addrtype != AF_INET) || (hp->h_length != 4)) {
-        *error = h_errno;
-        hp = NULL;
+               *error = h_errno;
+               hp = NULL;
        } else {
-        copy_hostent(hp, result, buffer, buflen, error);
-        hp = result;
+               copy_hostent(hp, result, buffer, buflen, error);
+               hp = result;
        }
 
-#ifdef HAVE_PTHREAD_H
+#  ifdef HAVE_PTHREAD_H
        pthread_mutex_unlock(&fr_hostbyname_mutex);
-#endif
+#  endif
 
        return hp;
 }
@@ -164,27 +164,26 @@ gethostbyname_r(char const *hostname, struct hostent *result,
 
 
 #ifdef LOCAL_GETHOSTBYADDRR
-static struct hostent *
-gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result,
-               char *buffer, int buflen, int *error)
+static struct hostent *gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result,
+                                      char *buffer, int buflen, int *error)
 {
        struct hostent *hp;
 
 #ifdef HAVE_PTHREAD_H
        if (fr_hostbyaddr == 0) {
-       pthread_mutex_init(&fr_hostbyaddr_mutex, NULL);
-       fr_hostbyaddr = 1;
+               pthread_mutex_init(&fr_hostbyaddr_mutex, NULL);
+               fr_hostbyaddr = 1;
        }
        pthread_mutex_lock(&fr_hostbyaddr_mutex);
 #endif
 
        hp = gethostbyaddr(addr, len, type);
        if ((!hp) || (hp->h_addrtype != AF_INET) || (hp->h_length != 4)) {
-        *error = h_errno;
-        hp = NULL;
+               *error = h_errno;
+               hp = NULL;
        } else {
-        copy_hostent(hp, result, buffer, buflen, error);
-        hp = result;
+               copy_hostent(hp, result, buffer, buflen, error);
+               hp = result;
        }
 
 #ifdef HAVE_PTHREAD_H
@@ -203,175 +202,173 @@ gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result,
  */
 
 #ifndef HAVE_GETADDRINFO
-static struct addrinfo *
-malloc_ai(uint16_t port, u_long addr, int socktype, int proto)
+static struct addrinfo *malloc_ai(uint16_t port, u_long addr, int socktype, int proto)
 {
        struct addrinfo *ai;
 
-       ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) +
-                                  sizeof(struct sockaddr_in));
-       if (ai) {
+       ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+       if (!ai) return NULL;
+
        memset(ai, 0, sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
        ai->ai_addr = (struct sockaddr *)(ai + 1);
        ai->ai_addrlen = sizeof(struct sockaddr_in);
-#ifdef HAVE_SOCKADDR_SA_LEN
+#  ifdef HAVE_SOCKADDR_SA_LEN
        ai->ai_addr->sa_len = sizeof(struct sockaddr_in);
-#endif
+#  endif
        ai->ai_addr->sa_family = ai->ai_family = AF_INET;
        ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
        ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
        ai->ai_socktype = socktype;
        ai->ai_protocol = proto;
+
        return ai;
-       } else {
-       return NULL;
-       }
 }
 
-char const *
-gai_strerror(int ecode)
+char const *gai_strerror(int ecode)
 {
        switch (ecode) {
        case EAI_MEMORY:
-       return "memory allocation failure.";
+               return "memory allocation failure";
+
        case EAI_FAMILY:
-       return "ai_family not supported.";
+               return "ai_family not supported";
+
        case EAI_NONAME:
-       return "hostname nor servname provided, or not known.";
+               return "hostname nor servname provided, or not known";
+
        case EAI_SERVICE:
-       return "servname not supported for ai_socktype.";
+               return "servname not supported for ai_socktype";
+
        default:
-       return "unknown error.";
+               return "unknown error";
        }
 }
 
-void
-freeaddrinfo(struct addrinfo *ai)
+void freeaddrinfo(struct addrinfo *ai)
 {
        struct addrinfo *next;
 
-       if (ai->ai_canonname)
-       free(ai->ai_canonname);
+       if (ai->ai_canonname) free(ai->ai_canonname);
+
        do {
-       next = ai->ai_next;
-       free(ai);
+               next = ai->ai_next;
+               free(ai);
        } while ((ai = next) != NULL);
 }
 
-int
-getaddrinfo(char const *hostname, char const *servname,
-               struct addrinfo const *hints, struct addrinfo **res)
+int getaddrinfo(char const *hostname, char const *servname, struct addrinfo const *hints, struct addrinfo **res)
 {
-       struct addrinfo *cur, *prev = NULL;
-       struct hostent *hp;
-       struct hostent result;
-       struct in_addr in;
-       int i, socktype, proto;
-       uint16_t port = 0;
-       int error;
-       char buffer[2048];
-
-       if (hints && hints->ai_family != PF_INET && hints->ai_family != PF_UNSPEC)
-       return EAI_FAMILY;
-
-       socktype = (hints && hints->ai_socktype) ? hints->ai_socktype
-                                                : SOCK_STREAM;
-       if (hints && hints->ai_protocol)
-       proto = hints->ai_protocol;
-       else {
-       switch (socktype) {
-       case SOCK_DGRAM:
-               proto = IPPROTO_UDP;
-               break;
-       case SOCK_STREAM:
-               proto = IPPROTO_TCP;
-               break;
-       default:
-               proto = 0;
-               break;
-       }
-       }
-       if (servname) {
-       if (isdigit((int)*servname))
-               port = htons(atoi(servname));
-       else {
-               struct servent *se;
-               char const *pe_proto;
-
+       struct addrinfo *cur, *prev = NULL;
+       struct hostent  *hp;
+       struct hostent  result;
+       struct in_addr  in;
+       int             i, socktype, proto;
+       uint16_t        port = 0;
+       int             error;
+       char            buffer[2048];
+
+       if (hints && (hints->ai_family != PF_INET) && (hints->ai_family != PF_UNSPEC)) return EAI_FAMILY;
+
+       socktype = (hints && hints->ai_socktype) ? hints->ai_socktype : SOCK_STREAM;
+       if (hints && hints->ai_protocol) {
+               proto = hints->ai_protocol;
+       } else {
                switch (socktype) {
                case SOCK_DGRAM:
-               pe_proto = "udp";
-               break;
+                       proto = IPPROTO_UDP;
+                       break;
                case SOCK_STREAM:
-               pe_proto = "tcp";
-               break;
+                       proto = IPPROTO_TCP;
+                       break;
                default:
-               pe_proto = NULL;
-               break;
+                       proto = 0;
+                       break;
                }
-               if ((se = getservbyname(servname, pe_proto)) == NULL)
-               return EAI_SERVICE;
-               port = se->s_port;
        }
+
+       if (servname) {
+               if (isdigit((int)*servname)) {
+                       port = htons(atoi(servname));
+               } else {
+                       struct          servent *se;
+                       char const      *pe_proto;
+
+                       switch (socktype) {
+                       case SOCK_DGRAM:
+                               pe_proto = "udp";
+                               break;
+
+                       case SOCK_STREAM:
+                               pe_proto = "tcp";
+                               break;
+
+                       default:
+                               pe_proto = NULL;
+                               break;
+                       }
+                       if ((se = getservbyname(servname, pe_proto)) == NULL) return EAI_SERVICE;
+
+                       port = se->s_port;
+               }
        }
+
        if (!hostname) {
-       if (hints && hints->ai_flags & AI_PASSIVE)
-               *res = malloc_ai(port, htonl(0x00000000), socktype, proto);
-       else
-               *res = malloc_ai(port, htonl(0x7f000001), socktype, proto);
-       if (*res)
+               if (hints && hints->ai_flags & AI_PASSIVE) {
+                       *res = malloc_ai(port, htonl(0x00000000), socktype, proto);
+               } else {
+                       *res = malloc_ai(port, htonl(0x7f000001), socktype, proto);
+               }
+               if (!*res) return EAI_MEMORY;
+
                return 0;
-       else
-               return EAI_MEMORY;
        }
+
        /* Numeric IP Address */
        if (inet_aton(hostname, &in)) {
-       *res = malloc_ai(port, in.s_addr, socktype, proto);
-       if (*res)
+               *res = malloc_ai(port, in.s_addr, socktype, proto);
+               if (!*res) return EAI_MEMORY;
+
                return 0;
-       else
-               return EAI_MEMORY;
        }
-       if (hints && hints->ai_flags & AI_NUMERICHOST)
-       return EAI_NONAME;
+
+       if (hints && hints->ai_flags & AI_NUMERICHOST) return EAI_NONAME;
 
        /* DNS Lookup */
 #ifdef GETHOSTBYNAMERSTYLE
-#if GETHOSTBYNAMERSTYLE == SYSVSTYLE
+#  if GETHOSTBYNAMERSTYLE == SYSVSTYLE
        hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
-#elif GETHOSTBYNAMERSTYLE == GNUSTYLE
-       if (gethostbyname_r(hostname, &result, buffer,
-        sizeof(buffer), &hp, &error) != 0) {
-               hp = NULL;
-       }
-#else
+#  elif GETHOSTBYNAMERSTYLE == GNUSTYLE
+       if (gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &hp, &error) != 0) hp = NULL;
+#  else
        hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
-#endif
+#  endif
 #else
        hp = gethostbyname_r(hostname, &result, buffer, sizeof(buffer), &error);
 #endif
+
        if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
-       for (i = 0; hp->h_addr_list[i]; i++) {
-               if ((cur = malloc_ai(port,
-                               ((struct in_addr *)hp->h_addr_list[i])->s_addr,
-                               socktype, proto)) == NULL) {
-               if (*res)
-                       freeaddrinfo(*res);
-               return EAI_MEMORY;
+               for (i = 0; hp->h_addr_list[i]; i++) {
+                       if ((cur = malloc_ai(port, ((struct in_addr *)hp->h_addr_list[i])->s_addr,
+                                            socktype, proto)) == NULL) {
+                               if (*res) freeaddrinfo(*res);
+                               return EAI_MEMORY;
+                       }
+
+                       if (prev) {
+                               prev->ai_next = cur;
+                       } else {
+                               *res = cur;
+                       }
+                       prev = cur;
                }
-               if (prev)
-               prev->ai_next = cur;
-               else
-               *res = cur;
-               prev = cur;
-       }
-       if (hints && hints->ai_flags & AI_CANONNAME && *res) {
-               if (((*res)->ai_canonname = strdup(hp->h_name)) == NULL) {
-               freeaddrinfo(*res);
-               return EAI_MEMORY;
+
+               if (hints && hints->ai_flags & AI_CANONNAME && *res) {
+                       if (((*res)->ai_canonname = strdup(hp->h_name)) == NULL) {
+                               freeaddrinfo(*res);
+                               return EAI_MEMORY;
+                       }
                }
-       }
-       return 0;
+               return 0;
        }
        return EAI_NONAME;
 }
@@ -379,75 +376,62 @@ getaddrinfo(char const *hostname, char const *servname,
 
 
 #ifndef HAVE_GETNAMEINFO
-int
-getnameinfo(struct sockaddr const  *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen,
+int getnameinfo(struct sockaddr const  *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen,
                unsigned int flags)
 {
-       const struct sockaddr_in *sin = (struct sockaddr_in const *)sa;
-       struct hostent *hp;
-       struct hostent result;
-       char tmpserv[16];
-       char buffer[2048];
-       int error;
+       const struct    sockaddr_in *sin = (struct sockaddr_in const *)sa;
+       struct hostent  *hp;
+       struct hostent  result;
+       char            tmpserv[16];
+       char            buffer[2048];
+       int             error;
 
        if (serv) {
-       snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
-       if (strlen(tmpserv) > servlen)
-               return EAI_MEMORY;
-       else
+               snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
+               if (strlen(tmpserv) > servlen) return EAI_MEMORY;
+
                strcpy(serv, tmpserv);
-       }
-       if (host) {
-       if (flags & NI_NUMERICHOST) {
-               /*  No Reverse DNS lookup */
-               if (flags & NI_NAMEREQD)
-               return EAI_NONAME;
-               if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen)
-               return EAI_MEMORY;
-               else {
-               strcpy(host, inet_ntoa(sin->sin_addr));
-               return 0;
-               }
-       } else {
+
+               if (host) {
+                       if (flags & NI_NUMERICHOST) {
+                               /*  No Reverse DNS lookup */
+                               if (flags & NI_NAMEREQD) return EAI_NONAME;
+                               if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY;
+
+                               strcpy(host, inet_ntoa(sin->sin_addr));
+                               return 0;
+                       } else {
        /*  Reverse DNS lookup required */
 #ifdef GETHOSTBYADDRRSTYLE
-#if GETHOSTBYADDRRSTYLE == SYSVSTYLE
-               hp = gethostbyaddr_r((char const *)&sin->sin_addr,
-                                  salen, AF_INET,
-                                  &result, buffer, sizeof(buffer), &error);
-#elif GETHOSTBYADDRRSTYLE == GNUSTYLE
-               if (gethostbyaddr_r((char const *)&sin->sin_addr,
-                                  salen, AF_INET,
-                                       &result, buffer, sizeof(buffer),
-                                       &hp, &error) != 0) {
-                       hp = NULL;
-                }
+#  if GETHOSTBYADDRRSTYLE == SYSVSTYLE
+                       hp = gethostbyaddr_r((char const *)&sin->sin_addr,
+                                            salen, AF_INET, &result, buffer, sizeof(buffer), &error);
+#  elif GETHOSTBYADDRRSTYLE == GNUSTYLE
+                       if (gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
+                                           &result, buffer, sizeof(buffer), &hp, &error) != 0) {
+                               hp = NULL;
+                       }
+#  else
+                       hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
+                                            &result, buffer, sizeof(buffer), &error);
+#  endif
 #else
-               hp = gethostbyaddr_r((char const *)&sin->sin_addr,
-                                  salen, AF_INET,
-                                  &result, buffer, sizeof(buffer), &error);
+                       hp = gethostbyaddr_r((char const *)&sin->sin_addr, salen, AF_INET,
+                                            &result, buffer, sizeof(buffer), &error);
 #endif
-#else
-               hp = gethostbyaddr_r((char const *)&sin->sin_addr,
-                                  salen, AF_INET,
-                                  &result, buffer, sizeof(buffer), &error);
-#endif
-               if (hp)
-               if (strlen(hp->h_name) >= hostlen)
-                       return EAI_MEMORY;
-               else {
-                       strcpy(host, hp->h_name);
+                       if (hp) {
+                               if (strlen(hp->h_name) >= hostlen) return EAI_MEMORY;
+
+                               strcpy(host, hp->h_name);
+                               return 0;
+                       }
+
+                       if (flags & NI_NAMEREQD) return EAI_NONAME;
+                       if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen) return EAI_MEMORY;
+
+                       strcpy(host, inet_ntoa(sin->sin_addr));
                        return 0;
                }
-               else if (flags & NI_NAMEREQD)
-               return EAI_NONAME;
-               else if (strlen(inet_ntoa(sin->sin_addr)) >= hostlen)
-               return EAI_MEMORY;
-               else {
-               strcpy(host, inet_ntoa(sin->sin_addr));
-               return 0;
-               }
-       }
        }
        return 0;
 }
index 7d91e07..9810431 100644 (file)
@@ -15,8 +15,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
index 5b2c923..2c662ff 100644 (file)
@@ -6,8 +6,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
index ec33e54..c3cbd87 100644 (file)
@@ -28,7 +28,7 @@ unsigned int sha1_data_problems = 0;
 void fr_hmac_sha1(uint8_t digest[SHA1_DIGEST_LENGTH], uint8_t const *text, size_t text_len,
                  uint8_t const *key, size_t key_len)
 {
-       fr_SHA1_CTX context;
+       fr_sha1_ctx context;
        uint8_t k_ipad[65];    /* inner padding - key XORd with ipad */
        uint8_t k_opad[65];    /* outer padding - key XORd with opad */
        uint8_t tk[20];
@@ -36,7 +36,7 @@ void fr_hmac_sha1(uint8_t digest[SHA1_DIGEST_LENGTH], uint8_t const *text, size_
        /* if key is longer than 64 bytes reset it to key=SHA1(key) */
        if (key_len > 64) {
 
-               fr_SHA1_CTX      tctx;
+               fr_sha1_ctx      tctx;
 
                fr_sha1_init(&tctx);
                fr_sha1_update(&tctx, key, key_len);
index 46f7a13..813e1ae 100644 (file)
@@ -6,8 +6,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -66,7 +65,7 @@ void fr_strerror_printf(char const *fmt, ...)
                /*
                 *      malloc is thread safe, talloc is not
                 */
-               buffer = malloc(sizeof(char) * (FR_STRERROR_BUFSIZE + 1));      /* One byte extra for status */
+               buffer = calloc((FR_STRERROR_BUFSIZE * 2) + 1, sizeof(char));   /* One byte extra for status */
                if (!buffer) {
                        fr_perror("Failed allocating memory for libradius error buffer");
                        return;
@@ -81,16 +80,29 @@ void fr_strerror_printf(char const *fmt, ...)
        }
 
        /*
-        *      NULL has a special meaning, setting the new byte to false.
+        *      NULL has a special meaning, setting the new bit to false.
         */
        if (!fmt) {
-               buffer[FR_STRERROR_BUFSIZE] = '\0';
+               buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;
                return;
        }
 
        va_start(ap, fmt);
-       vsnprintf(buffer, FR_STRERROR_BUFSIZE, fmt, ap);
-       buffer[FR_STRERROR_BUFSIZE] = '\1';                     /* Flip the 'new' byte to true */
+       /*
+        *      Alternate where we write the message, so we can do:
+        *      fr_strerror_printf("Additional error: %s", fr_strerror());
+        */
+       switch (buffer[FR_STRERROR_BUFSIZE * 2] & 0x06) {
+       default:
+               vsnprintf(buffer + FR_STRERROR_BUFSIZE, FR_STRERROR_BUFSIZE, fmt, ap);
+               buffer[FR_STRERROR_BUFSIZE * 2] = 0x05;                 /* Flip the 'new' bit to true */
+               break;
+
+       case 0x04:
+               vsnprintf(buffer, FR_STRERROR_BUFSIZE, fmt, ap);
+               buffer[FR_STRERROR_BUFSIZE * 2] = 0x03;                 /* Flip the 'new' bit to true */
+               break;
+       }
        va_end(ap);
 }
 
@@ -105,12 +117,20 @@ char const *fr_strerror(void)
        char *buffer;
 
        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 */
+       if (!buffer) return "";
+
+       switch (buffer[FR_STRERROR_BUFSIZE * 2]) {
+       default:
+               return "";
+
+       case 0x03:
+               buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;                /* Flip the 'new' bit to false */
                return buffer;
-       }
 
-       return "";
+       case 0x05:
+               buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;                /* Flip the 'new' bit to false */
+               return buffer + FR_STRERROR_BUFSIZE;
+       }
 }
 
 /** Guaranteed to be thread-safe version of strerror
@@ -149,8 +169,8 @@ char const *fr_syserror(int num)
        /*
         *      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)) {
+#if !defined(HAVE_FEATURES_H) || !defined(__GLIBC__) || ((_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));
@@ -167,7 +187,7 @@ char const *fr_syserror(int num)
 #else
        {
                char const *p;
-               p = strerror_r(num, buffer, (size_t) FR_STRERROR_BUFSIZE);
+               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 "
index 34b6b34..0515fca 100644 (file)
@@ -1,10 +1,10 @@
-/*
- *  md4c.c     MD4 message-digest algorithm
+/**
+ * $Id$
  *
- *  Version:   $Id$
+ * @note license is LGPL, but largely derived from a public domain source.
  *
- *  This file is licensed under the LGPL, but is largely derived
- *  from public domain source code.
+ * @file md4.c
+ * @brief md4 digest functions.
  */
 
 RCSID("$Id$")
@@ -15,31 +15,22 @@ RCSID("$Id$")
  */
 #include <freeradius-devel/md4.h>
 
-void fr_md4_calc(output, input, inlen)
-unsigned char *output;
-const unsigned char *input;                   /* input block */
-unsigned int inlen;                 /* length of input block */
+/** Calculate the MD4 hash of the contents of a buffer
+ *
+ * @param[out] out Where to write the MD4 digest. Must be a minimum of MD4_DIGEST_LENGTH.
+ * @param[in] in Data to hash.
+ * @param[in] inlen Length of the data.
+ */
+void fr_md4_calc(uint8_t out[MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
 {
-       FR_MD4_CTX      context;
+       FR_MD4_CTX ctx;
 
-       fr_md4_init(&context);
-       fr_md4_update(&context, input, inlen);
-       fr_md4_final(output, &context);
+       fr_md4_init(&ctx);
+       fr_md4_update(&ctx, in, inlen);
+       fr_md4_final(out, &ctx);
 }
 
 #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:
- *     CVS-$OpenBSD stuff deleted
- *     #includes commented out.
- *     Support context->count as uint32_t[2] instead of uint64_t
- *     Add htole32 define from http://www.squid-cache.org/mail-archive/squid-dev/200307/0130.html
- *             (The bswap32 definition in the patch.)
- *             This is only used on BIG_ENDIAN systems, so we can always swap the bits.
- *     change BYTE_ORDER == LITTLE_ENDIAN (OpenBSD-defined) to WORDS_BIGENDIAN (autoconf-defined)
- */
-
 /*
  * This code implements the MD4 message-digest algorithm.
  * The algorithm is due to Ron Rivest. This code was
@@ -58,76 +49,70 @@ unsigned int inlen;              /* length of input block */
  * will fill a supplied 16-byte array with the digest.
  */
 
-/*#include <sys/types.h>*/
-/*#include <string.h>*/
-/*#include <md4.h>*/
-
-/*#if BYTE_ORDER == LITTLE_ENDIAN*/
-#ifndef WORDS_BIGENDIAN
-
-#define htole32_4(buf)         /* Nothing */
-#define htole32_14(buf)                /* Nothing */
-#define htole32_16(buf)                /* Nothing */
-
+#ifdef FR_LITTLE_ENDIAN
+#  define htole32_4(buf)               /* Nothing */
+#  define htole32_14(buf)              /* Nothing */
+#  define htole32_16(buf)              /* Nothing */
 #else
-
-#define htole32(x) \
- (((((uint32_t)x) & 0xff000000) >> 24) | \
- ((((uint32_t)x) & 0x00ff0000) >> 8) | \
- ((((uint32_t)x) & 0x0000ff00) << 8) | \
- ((((uint32_t)x) & 0x000000ff) << 24))
-
-#define htole32_4(buf) do {                                            \
-       (buf)[ 0] = htole32((buf)[ 0]);                                 \
-       (buf)[ 1] = htole32((buf)[ 1]);                                 \
-       (buf)[ 2] = htole32((buf)[ 2]);                                 \
-       (buf)[ 3] = htole32((buf)[ 3]);                                 \
+/* Sometimes defined by endian.h */
+#  ifndef htole32
+#    define htole32(x)\
+       (((((uint32_t)x) & 0xff000000) >> 24) |\
+       ((((uint32_t)x) & 0x00ff0000) >> 8) |\
+       ((((uint32_t)x) & 0x0000ff00) << 8) |\
+       ((((uint32_t)x) & 0x000000ff) << 24))
+#  endif
+#  define htole32_4(buf) do {\
+       (buf)[0] = htole32((buf)[0]);\
+       (buf)[1] = htole32((buf)[1]);\
+       (buf)[2] = htole32((buf)[2]);\
+       (buf)[3] = htole32((buf)[3]);\
 } while (0)
 
-#define htole32_14(buf) do {                                           \
-       (buf)[ 0] = htole32((buf)[ 0]);                                 \
-       (buf)[ 1] = htole32((buf)[ 1]);                                 \
-       (buf)[ 2] = htole32((buf)[ 2]);                                 \
-       (buf)[ 3] = htole32((buf)[ 3]);                                 \
-       (buf)[ 4] = htole32((buf)[ 4]);                                 \
-       (buf)[ 5] = htole32((buf)[ 5]);                                 \
-       (buf)[ 6] = htole32((buf)[ 6]);                                 \
-       (buf)[ 7] = htole32((buf)[ 7]);                                 \
-       (buf)[ 8] = htole32((buf)[ 8]);                                 \
-       (buf)[ 9] = htole32((buf)[ 9]);                                 \
-       (buf)[10] = htole32((buf)[10]);                                 \
-       (buf)[11] = htole32((buf)[11]);                                 \
-       (buf)[12] = htole32((buf)[12]);                                 \
-       (buf)[13] = htole32((buf)[13]);                                 \
+#  define htole32_14(buf) do {\
+       (buf)[0] = htole32((buf)[0]);\
+       (buf)[1] = htole32((buf)[1]);\
+       (buf)[2] = htole32((buf)[2]);\
+       (buf)[3] = htole32((buf)[3]);\
+       (buf)[4] = htole32((buf)[4]);\
+       (buf)[5] = htole32((buf)[5]);\
+       (buf)[6] = htole32((buf)[6]);\
+       (buf)[7] = htole32((buf)[7]);\
+       (buf)[8] = htole32((buf)[8]);\
+       (buf)[9] = htole32((buf)[9]);\
+       (buf)[10] = htole32((buf)[10]);\
+       (buf)[11] = htole32((buf)[11]);\
+       (buf)[12] = htole32((buf)[12]);\
+       (buf)[13] = htole32((buf)[13]);\
 } while (0)
 
-#define htole32_16(buf) do {                                           \
-       (buf)[ 0] = htole32((buf)[ 0]);                                 \
-       (buf)[ 1] = htole32((buf)[ 1]);                                 \
-       (buf)[ 2] = htole32((buf)[ 2]);                                 \
-       (buf)[ 3] = htole32((buf)[ 3]);                                 \
-       (buf)[ 4] = htole32((buf)[ 4]);                                 \
-       (buf)[ 5] = htole32((buf)[ 5]);                                 \
-       (buf)[ 6] = htole32((buf)[ 6]);                                 \
-       (buf)[ 7] = htole32((buf)[ 7]);                                 \
-       (buf)[ 8] = htole32((buf)[ 8]);                                 \
-       (buf)[ 9] = htole32((buf)[ 9]);                                 \
-       (buf)[10] = htole32((buf)[10]);                                 \
-       (buf)[11] = htole32((buf)[11]);                                 \
-       (buf)[12] = htole32((buf)[12]);                                 \
-       (buf)[13] = htole32((buf)[13]);                                 \
-       (buf)[14] = htole32((buf)[14]);                                 \
-       (buf)[15] = htole32((buf)[15]);                                 \
+#  define htole32_16(buf) do {\
+       (buf)[0] = htole32((buf)[0]);\
+       (buf)[1] = htole32((buf)[1]);\
+       (buf)[2] = htole32((buf)[2]);\
+       (buf)[3] = htole32((buf)[3]);\
+       (buf)[4] = htole32((buf)[4]);\
+       (buf)[5] = htole32((buf)[5]);\
+       (buf)[6] = htole32((buf)[6]);\
+       (buf)[7] = htole32((buf)[7]);\
+       (buf)[8] = htole32((buf)[8]);\
+       (buf)[9] = htole32((buf)[9]);\
+       (buf)[10] = htole32((buf)[10]);\
+       (buf)[11] = htole32((buf)[11]);\
+       (buf)[12] = htole32((buf)[12]);\
+       (buf)[13] = htole32((buf)[13]);\
+       (buf)[14] = htole32((buf)[14]);\
+       (buf)[15] = htole32((buf)[15]);\
 } while (0)
-
 #endif
 
-/*
- * Start MD4 accumulation.
+/** Initialise a new MD4 context
+ *
  * Set bit count to 0 and buffer to mysterious initialization constants.
+ *
+ * @param[out] ctx to initialise.
  */
-void
-fr_md4_init(FR_MD4_CTX *ctx)
+void fr_md4_init(FR_MD4_CTX *ctx)
 {
        ctx->count[0] = 0;
        ctx->count[1] = 0;
@@ -137,12 +122,13 @@ fr_md4_init(FR_MD4_CTX *ctx)
        ctx->state[3] = 0x10325476;
 }
 
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
+/** Feed additional data into the MD4 hashing function
+ *
+ * @param[in,out] ctx to update.
+ * @param[in] in Data to hash.
+ * @param[in] inlen Length of the data.
  */
-void
-fr_md4_update(FR_MD4_CTX *ctx, unsigned char const *buf, size_t len)
+void fr_md4_update(FR_MD4_CTX *ctx, uint8_t const *in, size_t inlen)
 {
        uint32_t count;
 
@@ -150,48 +136,51 @@ fr_md4_update(FR_MD4_CTX *ctx, unsigned char const *buf, size_t len)
        count = (uint32_t)((ctx->count[0] >> 3) & 0x3f);
 
        /* Update bitcount */
-/*     ctx->count += (uint64_t)len << 3;*/
-       if ((ctx->count[0] += ((uint32_t)len << 3)) < (uint32_t)len) {
-       /* Overflowed ctx->count[0] */
+/*     ctx->count += (uint64_t)inlen << 3;*/
+       if ((ctx->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) {
+               /* Overflowed ctx->count[0] */
                ctx->count[1]++;
        }
-       ctx->count[1] += ((uint32_t)len >> 29);
+       ctx->count[1] += ((uint32_t)inlen >> 29);
 
        /* Handle any leading odd-sized chunks */
        if (count) {
                unsigned char *p = (unsigned char *)ctx->buffer + count;
 
                count = MD4_BLOCK_LENGTH - count;
-               if (len < count) {
-                       memcpy(p, buf, len);
+               if (inlen < count) {
+                       memcpy(p, in, inlen);
                        return;
                }
-               memcpy(p, buf, count);
+               memcpy(p, in, count);
                htole32_16((uint32_t *)ctx->buffer);
                fr_md4_transform(ctx->state, ctx->buffer);
-               buf += count;
-               len -= count;
+               in += count;
+               inlen -= count;
        }
 
        /* Process data in MD4_BLOCK_LENGTH-byte chunks */
-       while (len >= MD4_BLOCK_LENGTH) {
-               memcpy(ctx->buffer, buf, MD4_BLOCK_LENGTH);
+       while (inlen >= MD4_BLOCK_LENGTH) {
+               memcpy(ctx->buffer, in, MD4_BLOCK_LENGTH);
                htole32_16((uint32_t *)ctx->buffer);
                fr_md4_transform(ctx->state, ctx->buffer);
-               buf += MD4_BLOCK_LENGTH;
-               len -= MD4_BLOCK_LENGTH;
+               in += MD4_BLOCK_LENGTH;
+               inlen -= MD4_BLOCK_LENGTH;
        }
 
        /* Handle any remaining bytes of data. */
-       memcpy(ctx->buffer, buf, len);
+       memcpy(ctx->buffer, in, inlen);
 }
 
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
- * 1 0* (64-bit count of bits processed, MSB-first)
+/** Finalise the MD4 context and write out the hash
+ *
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 1 0*
+ * (64-bit count of bits processed, MSB-first).
+ *
+ * @param[out] out Where to write the MD4 digest. Minimum length of MD4_DIGEST_LENGTH.
+ * @param[in,out] ctx to finalise.
  */
-void
-fr_md4_final(unsigned char digest[MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
+void fr_md4_final(uint8_t out[MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
 {
        uint32_t count;
        unsigned char *p;
@@ -230,37 +219,36 @@ fr_md4_final(unsigned char digest[MD4_DIGEST_LENGTH], FR_MD4_CTX *ctx)
 
        fr_md4_transform(ctx->state, ctx->buffer);
        htole32_4(ctx->state);
-       memcpy(digest, ctx->state, MD4_DIGEST_LENGTH);
+       memcpy(out, ctx->state, MD4_DIGEST_LENGTH);
        memset(ctx, 0, sizeof(*ctx));   /* in case it's sensitive */
 }
 
-
 /* The three core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
 #define F1(x, y, z) (z ^ (x & (y ^ z)))
 #define F2(x, y, z) ((x & y) | (x & z) | (y & z))
 #define F3(x, y, z) (x ^ y ^ z)
 
 /* This is the central step in the MD4 algorithm. */
-#define MD4STEP(f, w, x, y, z, data, s) \
-       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s) )
+#define MD4STEP(f, w, x, y, z, data, s) (w += f(x, y, z) + data, w = w << s | w >> (32 - s))
 
-/*
- * The core of the MD4 algorithm, this alters an existing MD4 hash to
- * reflect the addition of 16 longwords of new data.  fr_md4_update blocks
- * the data and converts bytes into longwords for this routine.
+/** The core of the MD4 algorithm
+ *
+ * This alters an existing MD4 hash to reflect the addition of 16
+ * longwords of new data.  fr_md4_update blocks the data and converts bytes
+ * into longwords for this routine.
+ *
+ * @param[in] state 16 bytes of data to feed into the hashing function.
+ * @param[in,out] block MD4 digest block to update.
  */
-void
-fr_md4_transform(uint32_t buf[4], unsigned char const inc[MD4_BLOCK_LENGTH])
+void fr_md4_transform(uint32_t state[4], uint8_t const block[MD4_BLOCK_LENGTH])
 {
        uint32_t a, b, c, d;
-       uint32_t const *in = (uint32_t const *)inc;
+       uint32_t const *in = (uint32_t const *)block;
 
-       a = buf[0];
-       b = buf[1];
-       c = buf[2];
-       d = buf[3];
+       a = state[0];
+       b = state[1];
+       c = state[2];
+       d = state[3];
 
        MD4STEP(F1, a, b, c, d, in[ 0],  3);
        MD4STEP(F1, d, a, b, c, in[ 1],  7);
@@ -313,9 +301,9 @@ fr_md4_transform(uint32_t buf[4], unsigned char const inc[MD4_BLOCK_LENGTH])
        MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11);
        MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15);
 
-       buf[0] += a;
-       buf[1] += b;
-       buf[2] += c;
-       buf[3] += d;
+       state[0] += a;
+       state[1] += b;
+       state[2] += c;
+       state[3] += d;
 }
 #endif
index 07aac9e..9858175 100644 (file)
@@ -1,10 +1,10 @@
-/*
- *  md5.c      MD5 message-digest algorithm
+/**
+ * $Id$
  *
- *  Version:   $Id$
+ * @note license is LGPL, but largely derived from a public domain source.
  *
- *  This file is licensed under the LGPL, but is largely derived
- *  from public domain source code.
+ * @file md5.c
+ * @brief md5 digest functions.
  */
 
 RCSID("$Id$")
@@ -17,26 +17,22 @@ RCSID("$Id$")
  */
 #include <freeradius-devel/md5.h>
 
-void fr_md5_calc(uint8_t *output, uint8_t const *input,
-                    unsigned int inlen)
+/** Calculate the MD5 hash of the contents of a buffer
+ *
+ * @param[out] out Where to write the MD5 digest. Must be a minimum of MD5_DIGEST_LENGTH.
+ * @param[in] in Data to hash.
+ * @param[in] inlen Length of the data.
+ */
+void fr_md5_calc(uint8_t *out, uint8_t const *in, size_t inlen)
 {
-       FR_MD5_CTX      context;
+       FR_MD5_CTX ctx;
 
-       fr_md5_init(&context);
-       fr_md5_update(&context, input, inlen);
-       fr_md5_final(output, &context);
+       fr_md5_init(&ctx);
+       fr_md5_update(&ctx, in, inlen);
+       fr_md5_final(out, &ctx);
 }
 
-
 #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:
- *     #includes commented out.
- *     Support context->count as uint32_t[2] instead of uint64_t
- *     u_int* to uint*
- */
-
 /*
  * This code implements the MD5 message-digest algorithm.
  * The algorithm is due to Ron Rivest. This code was
@@ -53,26 +49,23 @@ void fr_md5_calc(uint8_t *output, uint8_t const *input,
  * needed on buffers full of bytes, and then call fr_md5_final, which
  * will fill a supplied 16-byte array with the digest.
  */
-
-/*#include <sys/param.h>*/
-/*#include <sys/systm.h>*/
-/*#include <crypto/md5.h>*/
-
-#define PUT_64BIT_LE(cp, value) do {           \
-       (cp)[7] = (value)[1] >> 24;             \
-       (cp)[6] = (value)[1] >> 16;             \
-       (cp)[5] = (value)[1] >> 8;              \
-       (cp)[4] = (value)[1];                   \
-       (cp)[3] = (value)[0] >> 24;             \
-       (cp)[2] = (value)[0] >> 16;             \
-       (cp)[1] = (value)[0] >> 8;              \
-       (cp)[0] = (value)[0]; } while (0)
-
-#define PUT_32BIT_LE(cp, value) do {           \
-       (cp)[3] = (value) >> 24;                \
-       (cp)[2] = (value) >> 16;                \
-       (cp)[1] = (value) >> 8;                 \
-       (cp)[0] = (value); } while (0)
+#define PUT_64BIT_LE(cp, value) do {\
+       (cp)[7] = (value)[1] >> 24;\
+       (cp)[6] = (value)[1] >> 16;\
+       (cp)[5] = (value)[1] >> 8;\
+       (cp)[4] = (value)[1];\
+       (cp)[3] = (value)[0] >> 24;\
+       (cp)[2] = (value)[0] >> 16;\
+       (cp)[1] = (value)[0] >> 8;\
+       (cp)[0] = (value)[0];\
+} while (0)
+
+#define PUT_32BIT_LE(cp, value) do {\
+       (cp)[3] = (value) >> 24;\
+       (cp)[2] = (value) >> 16;\
+       (cp)[1] = (value) >> 8;\
+       (cp)[0] = (value);\
+} while (0)
 
 static const uint8_t PADDING[MD5_BLOCK_LENGTH] = {
        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -80,12 +73,11 @@ static const uint8_t PADDING[MD5_BLOCK_LENGTH] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
-/** Start MD5 accumulation
+/** Initialise a new MD5 context
  *
- * Set bit count to 0 and buffer to mysterious
- * initialization constants.
+ * Set bit count to 0 and buffer to mysterious initialization constants.
  *
- * @param ctx MD5 context to be initialized.
+ * @param[out] ctx to initialise.
  */
 void fr_md5_init(FR_MD5_CTX *ctx)
 {
@@ -97,16 +89,13 @@ void fr_md5_init(FR_MD5_CTX *ctx)
        ctx->state[3] = 0x10325476;
 }
 
-/** Hash more data into the MD5 context
+/** Feed additional data into the MD5 hashing function
  *
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- *
- * @param ctx MD5 hashing context to update
- * @param input Data to add to hash
- * @param len Data length
+ * @param[in,out] ctx to update.
+ * @param[in] in Data to hash.
+ * @param[in] inlen Length of the data.
  */
-void fr_md5_update(FR_MD5_CTX *ctx, unsigned char const *input, size_t len)
+void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen)
 {
        size_t have, need;
 
@@ -115,40 +104,43 @@ void fr_md5_update(FR_MD5_CTX *ctx, unsigned char const *input, size_t len)
        need = MD5_BLOCK_LENGTH - have;
 
        /* Update bitcount */
-/*     ctx->count += (uint64_t)len << 3;*/
-       if ((ctx->count[0] += ((uint32_t)len << 3)) < (uint32_t)len) {
+/*     ctx->count += (uint64_t)inlen << 3;*/
+       if ((ctx->count[0] += ((uint32_t)inlen << 3)) < (uint32_t)inlen) {
        /* Overflowed ctx->count[0] */
                ctx->count[1]++;
        }
-       ctx->count[1] += ((uint32_t)len >> 29);
+       ctx->count[1] += ((uint32_t)inlen >> 29);
 
-       if (len >= need) {
+       if (inlen >= need) {
                if (have != 0) {
-                       memcpy(ctx->buffer + have, input, need);
+                       memcpy(ctx->buffer + have, in, need);
                        fr_md5_transform(ctx->state, ctx->buffer);
-                       input += need;
-                       len -= need;
+                       in += need;
+                       inlen -= need;
                        have = 0;
                }
 
                /* Process data in MD5_BLOCK_LENGTH-byte chunks. */
-               while (len >= MD5_BLOCK_LENGTH) {
-                       fr_md5_transform(ctx->state, input);
-                       input += MD5_BLOCK_LENGTH;
-                       len -= MD5_BLOCK_LENGTH;
+               while (inlen >= MD5_BLOCK_LENGTH) {
+                       fr_md5_transform(ctx->state, in);
+                       in += MD5_BLOCK_LENGTH;
+                       inlen -= MD5_BLOCK_LENGTH;
                }
        }
 
        /* Handle any remaining bytes of data. */
-       if (len != 0)
-               memcpy(ctx->buffer + have, input, len);
+       if (inlen != 0) memcpy(ctx->buffer + have, in, inlen);
 }
 
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
- * 1 0* (64-bit count of bits processed, MSB-first)
+/** Finalise the MD5 context and write out the hash
+ *
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 1 0*
+ * (64-bit count of bits processed, MSB-first).
+ *
+ * @param[out] out Where to write the MD5 digest. Minimum length of MD5_DIGEST_LENGTH.
+ * @param[in,out] ctx to finalise.
  */
-void fr_md5_final(uint8_t digest[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx)
+void fr_md5_final(uint8_t out[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx)
 {
        uint8_t count[8];
        size_t padlen;
@@ -165,29 +157,30 @@ void fr_md5_final(uint8_t digest[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx)
        fr_md5_update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
        fr_md5_update(ctx, count, 8);
 
-       if (digest != NULL) {
+       if (out != NULL) {
                for (i = 0; i < 4; i++)
-                       PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
+                       PUT_32BIT_LE(out + i * 4, ctx->state[i]);
        }
        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) */
 #define F1(x, y, z) (z ^ (x & (y ^ z)))
 #define F2(x, y, z) F1(z, x, y)
 #define F3(x, y, z) (x ^ y ^ z)
 #define F4(x, y, z) (y ^ (x | ~z))
 
 /* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f, w, x, y, z, data, s) \
-       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+#define MD5STEP(f, w, x, y, z, data, s) (w += f(x, y, z) + data, w = w << s | w >> (32 - s),  w += x)
 
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data.  fr_md5_update blocks
- * the data and converts bytes into longwords for this routine.
+/** The core of the MD5 algorithm
+ *
+ * This alters an existing MD5 hash to reflect the addition of 16
+ * longwords of new data.  fr_md5_update blocks the data and converts bytes
+ * into longwords for this routine.
+ *
+ * @param[in] state 16 bytes of data to feed into the hashing function.
+ * @param[in,out] block MD5 digest block to update.
  */
 void fr_md5_transform(uint32_t state[4], uint8_t const block[MD5_BLOCK_LENGTH])
 {
index da43371..c093898 100644 (file)
@@ -5,8 +5,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -30,6 +29,7 @@ RCSID("$Id$")
 #include <fcntl.h>
 #include <grp.h>
 #include <pwd.h>
+#include <sys/uio.h>
 
 #define FR_PUT_LE16(a, val)\
        do {\
@@ -39,7 +39,7 @@ RCSID("$Id$")
 
 bool   fr_dns_lookups = false;     /* IP -> hostname lookups? */
 bool    fr_hostname_lookups = true; /* hostname -> IP lookups? */
-int    fr_debug_flag = 0;
+int    fr_debug_lvl = 0;
 
 static char const *months[] = {
        "jan", "feb", "mar", "apr", "may", "jun",
@@ -226,6 +226,7 @@ int fr_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, b
                }
                memcpy(buffer, value, inlen);
                buffer[inlen] = '\0';
+               value = buffer;
        }
 
        p = strchr(value, '/');
@@ -329,6 +330,7 @@ int fr_pton6(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, b
                }
                memcpy(buffer, value, inlen);
                buffer[inlen] = '\0';
+               value = buffer;
        }
 
        p = strchr(value, '/');
@@ -395,25 +397,29 @@ int fr_pton6(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, b
 
 /** 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 -1.
- * @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.
+ * @param[out] out Where to write the ip address value.
+ * @param[in] value to parse.
+ * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1.
+ * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value as a
+ *     hostname.
+ * @param[in] af If the address type is not obvious from the format, and resolve is true, the DNS
+ *     record (A or AAAA) we require.  Also controls which parser we pass the address to if
+ *     we have no idea what it is.
+ * @return
+ *     - 0 if ip address was parsed successfully.
+ *     - -1 on failure.
  */
-int fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve)
+int fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve)
 {
        size_t len, i;
 
        len = (inlen >= 0) ? (size_t)inlen : strlen(value);
        for (i = 0; i < len; i++) switch (value[i]) {
        /*
-        *      Chars illegal in domain names and IPv4 addresses.
+        *      ':' is 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);
 
        /*
@@ -429,8 +435,24 @@ int fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve)
                 *      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);
+                       if (!resolve) {
+                               fr_strerror_printf("Not IPv4/6 address, and asked not to resolve");
+                               return -1;
+                       }
+                       switch (af) {
+                       case AF_UNSPEC:
+                               return fr_pton4(out, value, inlen, resolve, true);
+
+                       case AF_INET:
+                               return fr_pton4(out, value, inlen, resolve, false);
+
+                       case AF_INET6:
+                               return fr_pton6(out, value, inlen, resolve, false);
+
+                       default:
+                               fr_strerror_printf("Invalid address family %i", af);
+                               return -1;
+                       }
                }
                break;
        }
@@ -442,6 +464,86 @@ int fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve)
        return fr_pton4(out, value, inlen, false, false);
 }
 
+/** Parses IPv4/6 address + port, to fr_ipaddr_t and integer
+ *
+ * @param[out] out Where to write the ip address value.
+ * @param[out] port_out Where to write the port (0 if no port found).
+ * @param[in] value to parse.
+ * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1.
+ * @param[in] af If the address type is not obvious from the format, and resolve is true, the DNS
+ *     record (A or AAAA) we require.  Also controls which parser we pass the address to if
+ *     we have no idea what it is.
+ * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value as a
+ *     hostname.
+ */
+int fr_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve)
+{
+       char const      *p = value, *q;
+       char            *end;
+       unsigned long   port;
+       char            buffer[6];
+       size_t          len;
+
+       *port_out = 0;
+
+       len = (inlen >= 0) ? (size_t)inlen : strlen(value);
+
+       if (*p == '[') {
+               if (!(q = memchr(p + 1, ']', len - 1))) {
+                       fr_strerror_printf("Missing closing ']' for IPv6 address");
+                       return -1;
+               }
+
+               /*
+                *      inet_pton doesn't like the address being wrapped in []
+                */
+               if (fr_pton6(out, p + 1, (q - p) - 1, false, false) < 0) return -1;
+
+               if (q[1] == ':') {
+                       q++;
+                       goto do_port;
+               }
+
+               return 0;
+       }
+
+       /*
+        *      Host, IPv4 or IPv6 with no port
+        */
+       q = memchr(p, ':', len);
+       if (!q || !memchr(p, '.', len)) return fr_pton(out, p, len, af, resolve);
+
+       /*
+        *      IPv4 or host, with port
+        */
+       if (fr_pton(out, p, (q - p), af, resolve) < 0) return -1;
+do_port:
+       /*
+        *      Valid ports are a maximum of 5 digits, so if the
+        *      input length indicates there are more than 5 chars
+        *      after the ':' then there's an issue.
+        */
+       if (inlen > ((q + sizeof(buffer)) - value)) {
+       error:
+               fr_strerror_printf("IP string contains trailing garbage after port delimiter");
+               return -1;
+       }
+
+       p = q + 1;                      /* Move to first digit */
+
+       strlcpy(buffer, p, (len - (p - value)) + 1);
+       port = strtoul(buffer, &end, 10);
+       if (*end != '\0') goto error;   /* Trailing garbage after integer */
+
+       if ((port > UINT16_MAX) || (port == 0)) {
+               fr_strerror_printf("Port %lu outside valid port range 1-" STRINGIFY(UINT16_MAX), port);
+               return -1;
+       }
+       *port_out = port;
+
+       return 0;
+}
+
 int fr_ntop(char *out, size_t outlen, fr_ipaddr_t *addr)
 {
        char buffer[INET6_ADDRSTRLEN];
@@ -643,18 +745,16 @@ static int inet_pton4(char const *src, struct in_addr *dst)
 
 
 #ifdef HAVE_STRUCT_SOCKADDR_IN6
-/* int
- * inet_pton6(src, dst)
- *     convert presentation level address to network order binary form.
- * return:
- *     1 if `src' is a valid [RFC1884 2.2] address, else 0.
- * notice:
- *     (1) does not touch `dst' unless it's returning 1.
- *     (2) :: in a full address is silently ignored.
- * credit:
- *     inspired by Mark Andrews.
- * author:
- *     Paul Vixie, 1996.
+/** Convert presentation level address to network order binary form
+ *
+ * @note Does not touch dst unless it's returning 1.
+ * @note :: in a full address is silently ignored.
+ * @note Inspired by Mark Andrews.
+ * @author Paul Vixie, 1996.
+ *
+ * @param src presentation level address.
+ * @param dst where to write output address.
+ * @return 1 if `src' is a valid [RFC1884 2.2] address, else 0.
  */
 static int inet_pton6(char const *src, unsigned char *dst)
 {
@@ -903,7 +1003,10 @@ int ip_hton(fr_ipaddr_t *out, int af, char const *hostname, bool fallback)
        rcode = fr_sockaddr2ipaddr((struct sockaddr_storage *)ai->ai_addr,
                                   ai->ai_addrlen, out, NULL);
        freeaddrinfo(res);
-       if (!rcode) return -1;
+       if (!rcode) {
+               fr_strerror_printf("Failed converting sockaddr to ipaddr");
+               return -1;
+       }
 
        return 0;
 }
@@ -1121,9 +1224,33 @@ bool is_whitespace(char const *value)
        return true;
 }
 
+/** Check whether the string is made up of printable UTF8 chars
+ *
+ * @param value to check.
+ * @param len of value.
+ *
+ * @return
+ *     - true if the string is printable.
+ *     - false if the string contains non printable chars
+ */
+ bool is_printable(void const *value, size_t len)
+ {
+       uint8_t const *p = value;
+       int     clen;
+       size_t  i;
+
+       for (i = 0; i < len; i++) {
+               clen = fr_utf8_char(p, len - i);
+               if (clen == 0) return false;
+               i += (size_t)clen;
+               p += clen;
+       }
+       return true;
+ }
+
 /** Check whether the string is all numbers
  *
- * @return true if the entirety of the string is are numebrs, else false.
+ * @return true if the entirety of the string is all numbers, else false.
  */
 bool is_integer(char const *value)
 {
@@ -1136,7 +1263,7 @@ bool is_integer(char const *value)
 
 /** Check whether the string is allzeros
  *
- * @return true if the entirety of the string is are numebrs, else false.
+ * @return true if the entirety of the string is all zeros, else false.
  */
 bool is_zero(char const *value)
 {
@@ -1293,6 +1420,165 @@ int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
        return 1;
 }
 
+#ifdef O_NONBLOCK
+/** Set O_NONBLOCK on a socket
+ *
+ * @note O_NONBLOCK is POSIX.
+ *
+ * @param fd to set nonblocking flag on.
+ * @return flags set on the socket, or -1 on error.
+ */
+int fr_nonblock(int fd)
+{
+       int flags;
+
+       flags = fcntl(fd, F_GETFL, NULL);
+       if (flags < 0)  {
+               fr_strerror_printf("Failure getting socket flags: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       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;
+}
+
+/** Unset O_NONBLOCK on a socket
+ *
+ * @note O_NONBLOCK is POSIX.
+ *
+ * @param fd to set nonblocking flag on.
+ * @return flags set on the socket, or -1 on error.
+ */
+int fr_blocking(int fd)
+{
+       int flags;
+
+       flags = fcntl(fd, F_GETFL, NULL);
+       if (flags < 0)  {
+               fr_strerror_printf("Failure getting socket flags: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       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)
+{
+       fr_strerror_printf("Non blocking sockets are not supported");
+       return -1;
+}
+int fr_blocking(UNUSED int fd)
+{
+       fr_strerror_printf("Non blocking sockets are not supported");
+       return -1;
+}
+#endif
+
+/** Write out a vector to a file descriptor
+ *
+ * Wraps writev, calling it as necessary. If timeout is not NULL,
+ * timeout is applied to each call that returns EAGAIN or EWOULDBLOCK
+ *
+ * @note Should only be used on nonblocking file descriptors.
+ * @note Socket should likely be closed on timeout.
+ * @note iovec may be modified in such a way that it's not re-usable.
+ * @note Leaves errno set to the last error that ocurred.
+ *
+ * @param fd to write to.
+ * @param vector to write.
+ * @param iovcnt number of elements in iovec.
+ * @param timeout how long to wait for fd to become writeable before timing out.
+ * @return number of bytes written, -1 on error.
+ */
+ssize_t fr_writev(int fd, struct iovec vector[], int iovcnt, struct timeval *timeout)
+{
+       struct iovec *vector_p = vector;
+       ssize_t total = 0;
+
+       while (iovcnt > 0) {
+               ssize_t wrote;
+
+               wrote = writev(fd, vector_p, iovcnt);
+               if (wrote > 0) {
+                       total += wrote;
+                       while (wrote > 0) {
+                               /*
+                                *      An entire vector element was written
+                                */
+                               if (wrote >= (ssize_t)vector_p->iov_len) {
+                                       iovcnt--;
+                                       wrote -= vector_p->iov_len;
+                                       vector_p++;
+                                       continue;
+                               }
+
+                               /*
+                                *      Partial vector element was written
+                                */
+                               vector_p->iov_len -= wrote;
+                               vector_p->iov_base = ((char *)vector_p->iov_base) + wrote;
+                               break;
+                       }
+                       continue;
+               } else if (wrote == 0) return total;
+
+               switch (errno) {
+               /* Write operation would block, use select() to implement a timeout */
+#if EWOULDBLOCK != EAGAIN
+               case EWOULDBLOCK:
+               case EAGAIN:
+#else
+               case EAGAIN:
+#endif
+               {
+                       int     ret;
+                       fd_set  write_set;
+
+                       FD_ZERO(&write_set);
+                       FD_SET(fd, &write_set);
+
+                       /* Don't let signals mess up the select */
+                       do {
+                               ret = select(fd + 1, NULL, &write_set, NULL, timeout);
+                       } while ((ret == -1) && (errno == EINTR));
+
+                       /* Select returned 0 which means it reached the timeout */
+                       if (ret == 0) {
+                               fr_strerror_printf("Write timed out");
+                               return -1;
+                       }
+
+                       /* Other select error */
+                       if (ret < 0) {
+                               fr_strerror_printf("Failed waiting on socket: %s", fr_syserror(errno));
+                               return -1;
+                       }
+
+                       /* select said a file descriptor was ready for writing */
+                       if (!fr_assert(FD_ISSET(fd, &write_set))) return -1;
+
+                       break;
+               }
+
+               default:
+                       return -1;
+               }
+       }
+
+       return total;
+}
+
 /** Convert UTF8 string to UCS2 encoding
  *
  * @note Borrowed from src/crypto/ms_funcs.c of wpa_supplicant project (http://hostap.epitest.fi/wpa_supplicant/)
@@ -1362,7 +1648,7 @@ size_t fr_prints_uint128(char *out, size_t outlen, uint128_t const num)
        uint64_t n[2];
        char *p = buff;
        int i;
-#ifdef RADIUS_LITTLE_ENDIAN
+#ifdef FR_LITTLE_ENDIAN
        const size_t l = 0;
        const size_t h = 1;
 #else
index 7783de0..4c45fb4 100644 (file)
@@ -6,8 +6,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -274,7 +273,7 @@ ntp2timeval(struct timeval *tv, char const *ntp)
        tv->tv_usec = usec / 4295; /* close enough */
 }
 
-#if !defined(HAVE_128BIT_INTEGERS) && defined(RADIUS_LITTLE_ENDIAN)
+#if !defined(HAVE_128BIT_INTEGERS) && defined(FR_LITTLE_ENDIAN)
 /** Swap byte order of 128 bit integer
  *
  * @param num 128bit integer to swap.
@@ -337,3 +336,23 @@ char *talloc_typed_asprintf(void const *t, char const *fmt, ...)
 
        return n;
 }
+
+/** Binary safe strndup function
+ *
+ * @param[in] t The talloc context o allocate new buffer in.
+ * @param[in] in String to dup, may contain embedded '\0'.
+ * @param[in] inlen Number of bytes to dup.
+ * @return duped string.
+ */
+char *talloc_bstrndup(void const *t, char const *in, size_t inlen)
+{
+       char *p;
+
+       p = talloc_array(t, char, inlen + 1);
+       if (!p) return NULL;
+       memcpy(p, in, inlen);
+       p[inlen] = '\0';
+
+       return p;
+}
+
index fb8efdd..2981bc1 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This program is 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.
+ *   it under the terms of the GNU General Public License, version 2 of 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
  * @brief Functions to parse raw packets.
  *
  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
- * @copyright 2014 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2014-2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
  */
  #include <freeradius-devel/libradius.h>
  #include <freeradius-devel/net.h>
 
+/** Check whether fr_link_layer_offset can process a link_layer
+ *
+ * @param link_layer to check.
+ * @return true if supported, else false.
+ */
+bool fr_link_layer_supported(int link_layer)
+{
+       switch (link_layer) {
+       case DLT_EN10MB:
+       case DLT_RAW:
+       case DLT_NULL:
+       case DLT_LOOP:
+       case DLT_LINUX_SLL:
+       case DLT_PFLOG:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
 /** Returns the length of the link layer header
  *
  * Libpcap does not include a decoding function to skip the L2 header, but it does
  *
  * @param data start of packet data.
  * @param len caplen.
- * @param link_type value returned from pcap_linktype.
+ * @param link_layer value returned from pcap_linktype.
  * @return the length of the header, or -1 on error.
  */
-ssize_t fr_link_layer_offset(uint8_t const *data, size_t len, int link_type)
+ssize_t fr_link_layer_offset(uint8_t const *data, size_t len, int link_layer)
 {
        uint8_t const *p = data;
 
-       switch (link_type) {
+       switch (link_layer) {
        case DLT_RAW:
                break;
 
@@ -53,7 +73,10 @@ ssize_t fr_link_layer_offset(uint8_t const *data, size_t len, int link_type)
        case DLT_LOOP:
                p += 4;
                if (((size_t)(p - data)) > len) {
-                       goto ood;
+               ood:
+                       fr_strerror_printf("Out of data, needed %zu bytes, have %zu bytes",
+                                          (size_t)(p - data), len);
+                       return -1;
                }
                break;
 
@@ -111,16 +134,11 @@ ssize_t fr_link_layer_offset(uint8_t const *data, size_t len, int link_type)
                break;
 
        default:
-               fr_strerror_printf("Unsupported link layer type %i", link_type);
+               fr_strerror_printf("Unsupported link layer type %i", link_layer);
        }
 
-       done:
+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
@@ -181,9 +199,9 @@ uint16_t fr_iph_checksum(uint8_t const *data, uint8_t ihl)
 {
        uint64_t sum = 0;
        uint16_t const *p = (uint16_t const *)data;
-       
+
        uint8_t nwords = (ihl << 1); /* number of 16-bit words */
-       
+
        for (sum = 0; nwords > 0; nwords--) {
                sum += *p++;
        }
index dea3849..e29deb1 100644 (file)
@@ -5,8 +5,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -48,6 +47,9 @@ int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b)
        if (a->id < b->id) return -1;
        if (a->id > b->id) return +1;
 
+       if (a->sockfd < b->sockfd) return -1;
+       if (a->sockfd > b->sockfd) return +1;
+
        /*
         *      Source ports are pretty much random.
         */
@@ -75,9 +77,6 @@ int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b)
         *      pretty much redundant.
         */
        rcode = (int) a->dst_port - (int) b->dst_port;
-       if (rcode != 0) return rcode;
-
-       rcode = a->sockfd - b->sockfd;
        return rcode;
 }
 
@@ -113,38 +112,15 @@ void fr_request_from_reply(RADIUS_PACKET *request,
 {
        request->sockfd = reply->sockfd;
        request->id = reply->id;
+#ifdef WITH_TCP
+       request->proto = reply->proto;
+#endif
        request->src_port = reply->dst_port;
        request->dst_port = reply->src_port;
        request->src_ipaddr = reply->dst_ipaddr;
        request->dst_ipaddr = reply->src_ipaddr;
 }
 
-#ifdef O_NONBLOCK
-int fr_nonblock(int fd)
-{
-       int flags;
-
-       flags = fcntl(fd, F_GETFL, NULL);
-       if (flags < 0)  {
-               fr_strerror_printf("Failure getting socket flags: %s", fr_syserror(errno));
-               return -1;
-       }
-
-       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.
  */
@@ -555,16 +531,30 @@ RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl,
        my_request.sockfd = reply->sockfd;
        my_request.id = reply->id;
 
-       if (ps->src_any) {
-               my_request.src_ipaddr = ps->src_ipaddr;
-       } else {
+#ifdef WITH_TCP
+       /*
+        *      TCP sockets are always bound to the correct src/dst IP/port
+        */
+       if (ps->proto == IPPROTO_TCP) {
                my_request.src_ipaddr = reply->dst_ipaddr;
+               my_request.src_port = reply->dst_port;
+       } else
+#endif
+       {
+               if (ps->src_any) {
+                       my_request.src_ipaddr = ps->src_ipaddr;
+               } else {
+                       my_request.src_ipaddr = reply->dst_ipaddr;
+               }
+               my_request.src_port = ps->src_port;
        }
-       my_request.src_port = ps->src_port;
 
        my_request.dst_ipaddr = reply->src_ipaddr;
        my_request.dst_port = reply->src_port;
 
+#ifdef WITH_TCP
+       my_request.proto = reply->proto;
+#endif
        request = &my_request;
 
        return rbtree_finddata(pl->tree, &request);
@@ -721,6 +711,15 @@ bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
                    (ps->src_port != request->src_port)) continue;
 
                /*
+                *      We don't care about the source IP, but this
+                *      socket is link local, and the requested
+                *      destination is not link local.  Ignore it.
+                */
+               if (src_any && (ps->src_ipaddr.af == AF_INET) &&
+                   (((ps->src_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) == 127) &&
+                   (((request->dst_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) != 127)) continue;
+
+               /*
                 *      We're sourcing from *, and they asked for a
                 *      specific source address: ignore it.
                 */
@@ -929,6 +928,9 @@ RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
                 */
 
                pl->last_recv = start;
+#ifdef WITH_TCP
+               packet->proto = pl->sockets[start].proto;
+#endif
                return packet;
        } while (start != pl->last_recv);
 
@@ -972,31 +974,39 @@ void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received)
         *      This really belongs in a utility library
         */
        if (is_radius_code(packet->code)) {
-               fprintf(fp, "%s %s Id %i from %s:%i to %s:%i length %zu\n",
+               fprintf(fp, "%s %s Id %i from %s%s%s:%i to %s%s%s:%i length %zu\n",
                        received ? "Received" : "Sent",
                        fr_packet_codes[packet->code],
                        packet->id,
+                       packet->src_ipaddr.af == AF_INET6 ? "[" : "",
                        inet_ntop(packet->src_ipaddr.af,
                                  &packet->src_ipaddr.ipaddr,
                                  src_ipaddr, sizeof(src_ipaddr)),
+                       packet->src_ipaddr.af == AF_INET6 ? "]" : "",
                        packet->src_port,
+                       packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
                        inet_ntop(packet->dst_ipaddr.af,
                                  &packet->dst_ipaddr.ipaddr,
                                  dst_ipaddr, sizeof(dst_ipaddr)),
+                       packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
                        packet->dst_port,
                        packet->data_len);
        } else {
-               fprintf(fp, "%s code %u Id %i from %s:%i to %s:%i length %zu\n",
+               fprintf(fp, "%s code %u Id %i from %s%s%s:%i to %s%s%s:%i length %zu\n",
                        received ? "Received" : "Sent",
                        packet->code,
                        packet->id,
+                       packet->src_ipaddr.af == AF_INET6 ? "[" : "",
                        inet_ntop(packet->src_ipaddr.af,
                                  &packet->src_ipaddr.ipaddr,
                                  src_ipaddr, sizeof(src_ipaddr)),
+                       packet->src_ipaddr.af == AF_INET6 ? "]" : "",
                        packet->src_port,
+                       packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
                        inet_ntop(packet->dst_ipaddr.af,
                                  &packet->dst_ipaddr.ipaddr,
                                  dst_ipaddr, sizeof(dst_ipaddr)),
+                       packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
                        packet->dst_port,
                        packet->data_len);
        }
index 362d713..ce5484f 100644 (file)
@@ -1,12 +1,11 @@
 /*
- * valuepair.c Functions to handle VALUE_PAIRs
+ * pair.c      Functions to handle VALUE_PAIRs
  *
  * Version:    $Id$
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -35,7 +34,7 @@ RCSID("$Id$")
  * @param vp to free.
  * @return 0
  */
-static int _pairfree(VALUE_PAIR *vp) {
+static int _fr_pair_free(VALUE_PAIR *vp) {
 #ifndef NDEBUG
        vp->vp_integer = 0xf4eef4ee;
 #endif
@@ -54,7 +53,7 @@ static int _pairfree(VALUE_PAIR *vp) {
  * @param[in] da Specifies the dictionary attribute to build the VP from.
  * @return a new value pair or NULL if an error occurred.
  */
-VALUE_PAIR *pairalloc(TALLOC_CTX *ctx, DICT_ATTR const *da)
+VALUE_PAIR *fr_pair_afrom_da(TALLOC_CTX *ctx, DICT_ATTR const *da)
 {
        VALUE_PAIR *vp;
 
@@ -79,7 +78,7 @@ VALUE_PAIR *pairalloc(TALLOC_CTX *ctx, DICT_ATTR const *da)
 
        vp->vp_length = da->flags.length;
 
-       talloc_set_destructor(vp, _pairfree);
+       talloc_set_destructor(vp, _fr_pair_free);
 
        return vp;
 }
@@ -100,7 +99,7 @@ VALUE_PAIR *pairalloc(TALLOC_CTX *ctx, DICT_ATTR const *da)
  * @param[in] vendor number.
  * @return the new valuepair or NULL on error.
  */
-VALUE_PAIR *paircreate(TALLOC_CTX *ctx, unsigned int attr, unsigned int vendor)
+VALUE_PAIR *fr_pair_afrom_num(TALLOC_CTX *ctx, unsigned int attr, unsigned int vendor)
 {
        DICT_ATTR const *da;
 
@@ -112,14 +111,14 @@ VALUE_PAIR *paircreate(TALLOC_CTX *ctx, unsigned int attr, unsigned int vendor)
                }
        }
 
-       return pairalloc(ctx, da);
+       return fr_pair_afrom_da(ctx, da);
 }
 
 /** Free memory used by a valuepair list.
  *
  * @todo TLV: needs to free all dependents of each VP freed.
  */
-void pairfree(VALUE_PAIR **vps)
+void fr_pair_list_free(VALUE_PAIR **vps)
 {
        VALUE_PAIR      *vp;
        vp_cursor_t     cursor;
@@ -143,7 +142,7 @@ void pairfree(VALUE_PAIR **vps)
  * @param vp to change DICT_ATTR of.
  * @return 0 on success (or if already unknown) else -1 on error.
  */
-int pair2unknown(VALUE_PAIR *vp)
+int fr_pair_to_unknown(VALUE_PAIR *vp)
 {
        DICT_ATTR const *da;
 
@@ -165,7 +164,7 @@ int pair2unknown(VALUE_PAIR *vp)
 /** Find the pair with the matching DAs
  *
  */
-VALUE_PAIR *pair_find_by_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag)
+VALUE_PAIR *fr_pair_find_by_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag)
 {
        vp_cursor_t     cursor;
 
@@ -182,7 +181,7 @@ VALUE_PAIR *pair_find_by_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag)
  *
  * @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 *fr_pair_find_by_num(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor, int8_t tag)
 {
        vp_cursor_t     cursor;
 
@@ -206,8 +205,7 @@ VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor, int
  *
  * @todo should take DAs and do a point comparison.
  */
-void pairdelete(VALUE_PAIR **first, unsigned int attr, unsigned int vendor,
-               int8_t tag)
+void fr_pair_delete_by_num(VALUE_PAIR **first, unsigned int attr, unsigned int vendor, int8_t tag)
 {
        VALUE_PAIR *i, *next;
        VALUE_PAIR **last = first;
@@ -232,7 +230,7 @@ void pairdelete(VALUE_PAIR **first, unsigned int attr, unsigned int vendor,
  * @param[in] first VP in linked list. Will add new VP to the end of this list.
  * @param[in] add VP to add to list.
  */
-void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
+void fr_pair_add(VALUE_PAIR **first, VALUE_PAIR *add)
 {
        VALUE_PAIR *i;
 
@@ -269,7 +267,7 @@ void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
  * @param[in,out] first VP in linked list. Will search and replace in this list.
  * @param[in] replace VP to replace.
  */
-void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
+void fr_pair_replace(VALUE_PAIR **first, VALUE_PAIR *replace)
 {
        VALUE_PAIR *i, *next;
        VALUE_PAIR **prev = first;
@@ -318,7 +316,7 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
        *prev = replace;
 }
 
-int8_t attrtagcmp(void const *a, void const *b)
+int8_t fr_pair_cmp_by_da_tag(void const *a, void const *b)
 {
        VALUE_PAIR const *my_a = a;
        VALUE_PAIR const *my_b = b;
@@ -338,7 +336,7 @@ int8_t attrtagcmp(void const *a, void const *b)
        return 0;
 }
 
-static void pairsort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **back)
+static void fr_pair_list_sort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **back)
 {
        VALUE_PAIR *fast;
        VALUE_PAIR *slow;
@@ -373,7 +371,7 @@ 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, fr_cmp_t cmp)
+static VALUE_PAIR *fr_pair_list_sort_merge(VALUE_PAIR *a, VALUE_PAIR *b, fr_cmp_t cmp)
 {
        VALUE_PAIR *result = NULL;
 
@@ -385,10 +383,10 @@ static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, fr_cmp_t cmp)
         */
        if (cmp(a, b) <= 0) {
                result = a;
-               result->next = pairsort_merge(a->next, b, cmp);
+               result->next = fr_pair_list_sort_merge(a->next, b, cmp);
        } else {
                result = b;
-               result->next = pairsort_merge(a, b->next, cmp);
+               result->next = fr_pair_list_sort_merge(a, b->next, cmp);
        }
 
        return result;
@@ -399,7 +397,7 @@ static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, fr_cmp_t cmp)
  * @param[in,out] vps List of VALUE_PAIRs to sort.
  * @param[in] cmp to sort with
  */
-void pairsort(VALUE_PAIR **vps, fr_cmp_t cmp)
+void fr_pair_list_sort(VALUE_PAIR **vps, fr_cmp_t cmp)
 {
        VALUE_PAIR *head = *vps;
        VALUE_PAIR *a;
@@ -412,14 +410,14 @@ void pairsort(VALUE_PAIR **vps, fr_cmp_t cmp)
                return;
        }
 
-       pairsort_split(head, &a, &b);   /* Split into sublists */
-       pairsort(&a, cmp);              /* Traverse left */
-       pairsort(&b, cmp);              /* Traverse right */
+       fr_pair_list_sort_split(head, &a, &b);  /* Split into sublists */
+       fr_pair_list_sort(&a, cmp);             /* Traverse left */
+       fr_pair_list_sort(&b, cmp);             /* Traverse right */
 
        /*
         *      merge the two sorted lists together
         */
-       *vps = pairsort_merge(a, b, cmp);
+       *vps = fr_pair_list_sort_merge(a, b, cmp);
 }
 
 /** Write an error to the library errorbuff detailing the mismatch
@@ -431,7 +429,7 @@ void pairsort(VALUE_PAIR **vps, fr_cmp_t cmp)
  * @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])
+void fr_pair_validate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2])
 {
        VALUE_PAIR const *filter = failed[0];
        VALUE_PAIR const *list = failed[1];
@@ -471,7 +469,7 @@ void pairvalidate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2])
        return;
 }
 
-/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
+/** Uses fr_pair_cmp to verify all VALUE_PAIRs in list match the filter defined by check
  *
  * @note will sort both filter and list in place.
  *
@@ -480,7 +478,7 @@ void pairvalidate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2])
  * @param filter attributes to check list against.
  * @param list attributes, probably a request or reply
  */
-bool pairvalidate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
+bool fr_pair_validate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
 {
        vp_cursor_t filter_cursor;
        vp_cursor_t list_cursor;
@@ -497,8 +495,8 @@ bool pairvalidate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *l
         *
         *      @todo this should be removed one we have sets and lists
         */
-       pairsort(&filter, attrtagcmp);
-       pairsort(&list, attrtagcmp);
+       fr_pair_list_sort(&filter, fr_pair_cmp_by_da_tag);
+       fr_pair_list_sort(&list, fr_pair_cmp_by_da_tag);
 
        check = fr_cursor_init(&filter_cursor, &filter);
        match = fr_cursor_init(&list_cursor, &list);
@@ -522,7 +520,7 @@ bool pairvalidate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *l
                 *      Note that the RFCs say that for attributes of
                 *      the same type, order is important.
                 */
-               if (paircmp(check, match) != 1) goto mismatch;
+               if (fr_pair_cmp(check, match) != 1) goto mismatch;
 
                check = fr_cursor_next(&filter_cursor);
                match = fr_cursor_next(&list_cursor);
@@ -538,7 +536,7 @@ mismatch:
        return false;
 }
 
-/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
+/** Uses fr_pair_cmp to verify all VALUE_PAIRs in list match the filter defined by check
  *
  * @note will sort both filter and list in place.
  *
@@ -547,7 +545,7 @@ mismatch:
  * @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)
+bool fr_pair_validate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
 {
        vp_cursor_t filter_cursor;
        vp_cursor_t list_cursor;
@@ -564,8 +562,8 @@ bool pairvalidate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE
         *
         *      @todo this should be removed one we have sets and lists
         */
-       pairsort(&filter, attrtagcmp);
-       pairsort(&list, attrtagcmp);
+       fr_pair_list_sort(&filter, fr_pair_cmp_by_da_tag);
+       fr_pair_list_sort(&list, fr_pair_cmp_by_da_tag);
 
        fr_cursor_init(&list_cursor, &list);
        for (check = fr_cursor_init(&filter_cursor, &filter);
@@ -598,7 +596,7 @@ bool pairvalidate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE
                        /*
                         *      This attribute passed the filter
                         */
-                       if (!paircmp(check, match)) goto mismatch;
+                       if (!fr_pair_cmp(check, match)) goto mismatch;
                }
        }
 
@@ -620,7 +618,7 @@ mismatch:
  * @param[in] vp to copy.
  * @return a copy of the input VP or NULL on error.
  */
-VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
+VALUE_PAIR *fr_pair_copy(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
 {
        VALUE_PAIR *n;
 
@@ -628,7 +626,7 @@ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
 
        VERIFY_VP(vp);
 
-       n = pairalloc(ctx, vp->da);
+       n = fr_pair_afrom_da(ctx, vp->da);
        if (!n) return NULL;
 
        memcpy(n, vp, sizeof(*n));
@@ -638,7 +636,7 @@ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
         *      nothing for "n", but will also copy the unknown "da".
         */
        if (n->da->flags.is_unknown) {
-               pairsteal(ctx, n);
+               fr_pair_steal(ctx, n);
        }
 
        n->next = NULL;
@@ -653,15 +651,14 @@ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
        }
 
        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->vp_length);
+               n->vp_octets = NULL;    /* else fr_pair_value_memcpy will free vp's value */
+               fr_pair_value_memcpy(n, vp->vp_octets, n->vp_length);
                break;
 
        case PW_TYPE_STRING:
                n->vp_strvalue = NULL;  /* else pairstrnpy will free vp's value */
-               pairstrncpy(n, vp->vp_strvalue, n->vp_length);
+               fr_pair_value_bstrncpy(n, vp->vp_strvalue, n->vp_length);
                break;
 
        default:
@@ -679,7 +676,7 @@ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
  * @param[in] from whence to copy VALUE_PAIRs.
  * @return the head of the new VALUE_PAIR list or NULL on error.
  */
-VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
+VALUE_PAIR *fr_pair_list_copy(TALLOC_CTX *ctx, VALUE_PAIR *from)
 {
        vp_cursor_t src, dst;
 
@@ -690,12 +687,12 @@ VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
             vp;
             vp = fr_cursor_next(&src)) {
                VERIFY_VP(vp);
-               vp = paircopyvp(ctx, vp);
+               vp = fr_pair_copy(ctx, vp);
                if (!vp) {
-                       pairfree(&out);
+                       fr_pair_list_free(&out);
                        return NULL;
                }
-               fr_cursor_insert(&dst, vp); /* paircopy sets next pointer to NULL */
+               fr_cursor_insert(&dst, vp); /* fr_pair_list_copy sets next pointer to NULL */
        }
 
        return out;
@@ -713,7 +710,8 @@ VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
  * @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_by_num(TALLOC_CTX *ctx, VALUE_PAIR *from, unsigned int attr, unsigned int vendor, int8_t tag)
+VALUE_PAIR *fr_pair_list_copy_by_num(TALLOC_CTX *ctx, VALUE_PAIR *from,
+                                    unsigned int attr, unsigned int vendor, int8_t tag)
 {
        vp_cursor_t src, dst;
 
@@ -729,13 +727,13 @@ VALUE_PAIR *paircopy_by_num(TALLOC_CTX *ctx, VALUE_PAIR *from, unsigned int attr
                        continue;
                }
 
-               if (vp->da->flags.has_tag && TAG_EQ(tag, vp->tag)) {
+               if (vp->da->flags.has_tag && !TAG_EQ(tag, vp->tag)) {
                        continue;
                }
 
-               vp = paircopyvp(ctx, vp);
+               vp = fr_pair_copy(ctx, vp);
                if (!vp) {
-                       pairfree(&out);
+                       fr_pair_list_free(&out);
                        return NULL;
                }
                fr_cursor_insert(&dst, vp);
@@ -749,7 +747,7 @@ VALUE_PAIR *paircopy_by_num(TALLOC_CTX *ctx, VALUE_PAIR *from, unsigned int attr
  * @param[in] ctx to move VALUE_PAIR into
  * @param[in] vp VALUE_PAIR to move into the new context.
  */
-void pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *vp)
+void fr_pair_steal(TALLOC_CTX *ctx, VALUE_PAIR *vp)
 {
        (void) talloc_steal(ctx, vp);
 
@@ -783,7 +781,7 @@ void pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *vp)
  * @note This function does some additional magic that's probably not needed
  *      in most places. Consider using radius_pairmove in server code.
  *
- * @note pairfree should be called on the head of the source list to free
+ * @note fr_pair_list_free should be called on the head of the source list to free
  *      unmoved attributes (if they're no longer needed).
  *
  * @note Does not respect tags when matching.
@@ -794,7 +792,7 @@ void pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *vp)
  *
  * @see radius_pairmove
  */
-void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
+void fr_pair_list_move(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
 {
        VALUE_PAIR *i, *found;
        VALUE_PAIR *head_new, **tail_new;
@@ -850,7 +848,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
                 *      it doesn't already exist.
                 */
                case T_OP_EQ:
-                       found = pair_find_by_da(*to, i->da, TAG_ANY);
+                       found = fr_pair_find_by_da(*to, i->da, TAG_ANY);
                        if (!found) goto do_add;
 
                        tail_from = &(i->next);
@@ -861,15 +859,15 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
                 *      of the same vendor/attr which already exists.
                 */
                case T_OP_SET:
-                       found = pair_find_by_da(*to, i->da, TAG_ANY);
+                       found = fr_pair_find_by_da(*to, i->da, TAG_ANY);
                        if (!found) goto do_add;
 
                        /*
-                        *      Do NOT call pairdelete() here,
+                        *      Do NOT call fr_pair_delete_by_num() here,
                         *      due to issues with re-writing
                         *      "request->username".
                         *
-                        *      Everybody calls pairmove, and
+                        *      Everybody calls fr_pair_move, and
                         *      expects it to work.  We can't
                         *      update request->username here,
                         *      so instead we over-write the
@@ -882,18 +880,13 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
                                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);
+                               fr_pair_value_memsteal(found, i->vp_octets);
                                i->vp_octets = NULL;
                                break;
 
                        case PW_TYPE_STRING:
-                               pairstrsteal(found, i->vp_strvalue);
+                               fr_pair_value_strsteal(found, i->vp_strvalue);
                                i->vp_strvalue = NULL;
                                found->tag = i->tag;
                                break;
@@ -903,7 +896,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
                         *      Delete *all* of the attributes
                         *      of the same number.
                         */
-                       pairdelete(&found->next,
+                       fr_pair_delete_by_num(&found->next,
                                   found->da->attr,
                                   found->da->vendor, TAG_ANY);
 
@@ -913,7 +906,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
                         */
                        *tail_from = i->next;
                        i->next = NULL;
-                       pairfree(&i);
+                       fr_pair_list_free(&i);
                        continue;
 
                /*
@@ -925,7 +918,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
                        *tail_from = i->next;
                        i->next = NULL;
                        *tail_new = i;
-                       pairsteal(ctx, i);
+                       fr_pair_steal(ctx, i);
                        tail_new = &(i->next);
                        continue;
                }
@@ -934,7 +927,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
        /*
         *      Take the "new" list, and append it to the "to" list.
         */
-       pairadd(to, head_new);
+       fr_pair_add(to, head_new);
 }
 
 /** Move matching pairs between VALUE_PAIR lists
@@ -942,9 +935,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
  * Move pairs of a matching attribute number, vendor number and tag from the
  * the input list to the output list.
  *
- * @note pairs which are moved have their parent changed to ctx.
- *
- * @note pairfree should be called on the head of the old list to free unmoved
+ * @note fr_pair_list_free should be called on the head of the old list to free unmoved
         attributes (if they're no longer needed).
  *
  * @param[in] ctx for talloc
@@ -956,10 +947,13 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
  *     attributes.
  * @param[in] vendor to match.
  * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
+ * @param[in] move if set to "true", VPs are moved.  If set to "false", VPs are copied, and the old one deleted.
  */
-void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned int attr, unsigned int vendor, int8_t tag)
+static void fr_pair_list_move_by_num_internal(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from,
+                                             unsigned int attr, unsigned int vendor, int8_t tag,
+                                             bool move)
 {
-       VALUE_PAIR *to_tail, *i, *next;
+       VALUE_PAIR *to_tail, *i, *next, *this;
        VALUE_PAIR *iprev = NULL;
 
        /*
@@ -978,7 +972,7 @@ void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned in
 
        /*
         *      Attr/vendor of 0 means "move them all".
-        *      It's better than "pairadd(foo,bar);bar=NULL"
+        *      It's better than "fr_pair_add(foo,bar);bar=NULL"
         */
        if ((vendor == 0) && (attr == 0)) {
                if (*to) {
@@ -988,7 +982,7 @@ void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned in
                }
 
                for (i = *from; i; i = i->next) {
-                       pairsteal(ctx, i);
+                       fr_pair_steal(ctx, i);
                }
 
                *from = NULL;
@@ -999,7 +993,8 @@ void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned in
                VERIFY_VP(i);
                next = i->next;
 
-               if (i->da->flags.has_tag && TAG_EQ(tag, i->tag)) {
+               if (i->da->flags.has_tag && !TAG_EQ(tag, i->tag)) {
+                       iprev = i;
                        continue;
                }
 
@@ -1042,19 +1037,86 @@ void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned in
                else
                        *from = next;
 
+               if (move) {
+                       this = i;
+               } else {
+                       this = fr_pair_copy(ctx, i);
+               }
+
                /*
                 *      Add the attribute to the "to" list.
                 */
                if (to_tail)
-                       to_tail->next = i;
+                       to_tail->next = this;
                else
-                       *to = i;
-               to_tail = i;
-               i->next = NULL;
-               pairsteal(ctx, i);
+                       *to = this;
+               to_tail = this;
+               this->next = NULL;
+
+               if (move) {
+                       fr_pair_steal(ctx, i);
+               } else {
+                       talloc_free(i);
+               }
        }
 }
 
+
+/** Move matching pairs between VALUE_PAIR lists
+ *
+ * Move pairs of a matching attribute number, vendor number and tag from the
+ * the input list to the output list.
+ *
+ * @note pairs which are moved have their parent changed to ctx.
+ *
+ * @note fr_pair_list_free should be called on the head of the old list to free unmoved
+        attributes (if they're no longer needed).
+ *
+ * @param[in] ctx for talloc
+ * @param[in,out] to destination list.
+ * @param[in,out] from source list.
+ * @param[in] attr to match. If attribute PW_VENDOR_SPECIFIC and vendor 0,
+ *     will match (and therefore copy) only VSAs.
+ *     If attribute 0 and vendor 0  will match (and therefore copy) all
+ *     attributes.
+ * @param[in] vendor to match.
+ * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
+ */
+void fr_pair_list_move_by_num(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from,
+                             unsigned int attr, unsigned int vendor, int8_t tag)
+{
+       fr_pair_list_move_by_num_internal(ctx, to, from, attr, vendor, tag, true);
+}
+
+
+/** Copy / delete matching pairs between VALUE_PAIR lists
+ *
+ * Move pairs of a matching attribute number, vendor number and tag from the
+ * the input list to the output list.  Like fr_pair_list_move_by_num(), but
+ * instead does copy / delete.
+ *
+ * @note The pair is NOT reparented.  It is copied and deleted.
+ *
+ * @note fr_pair_list_free should be called on the head of the old list to free unmoved
+        attributes (if they're no longer needed).
+ *
+ * @param[in] ctx for talloc
+ * @param[in,out] to destination list.
+ * @param[in,out] from source list.
+ * @param[in] attr to match. If attribute PW_VENDOR_SPECIFIC and vendor 0,
+ *     will match (and therefore copy) only VSAs.
+ *     If attribute 0 and vendor 0  will match (and therefore copy) all
+ *     attributes.
+ * @param[in] vendor to match.
+ * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
+ */
+void fr_pair_list_mcopy_by_num(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from,
+                             unsigned int attr, unsigned int vendor, int8_t tag)
+{
+       fr_pair_list_move_by_num_internal(ctx, to, from, attr, vendor, tag, false);
+}
+
+
 /** Convert string value to native attribute value
  *
  * @param vp to assign value to.
@@ -1063,7 +1125,7 @@ void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned in
  *       should be the length of the string or sub string to parse.
  * @return 0 on success -1 on error.
  */
-int pairparsevalue(VALUE_PAIR *vp, char const *value, size_t inlen)
+int fr_pair_value_from_str(VALUE_PAIR *vp, char const *value, size_t inlen)
 {
        ssize_t ret;
        PW_TYPE type;
@@ -1117,8 +1179,8 @@ int pairparsevalue(VALUE_PAIR *vp, char const *value, size_t inlen)
  * @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 *fr_pair_afrom_ip_str(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;
@@ -1127,7 +1189,7 @@ VALUE_PAIR *pairmake_ip(TALLOC_CTX *ctx, char const *value, DICT_ATTR *ipv4, DIC
                return NULL;
        }
 
-       /* No point in repeating the work of pairparsevalue */
+       /* No point in repeating the work of fr_pair_value_from_str */
        if (strchr(value, ':')) {
                if (strchr(value, '/')) {
                        da = ipv6_prefix;
@@ -1153,9 +1215,9 @@ VALUE_PAIR *pairmake_ip(TALLOC_CTX *ctx, char const *value, DICT_ATTR *ipv4, DIC
                           ipv4_prefix ? "ipv4prefix " : "", ipv6_prefix ? "ipv6prefix" : "");
 
 finish:
-       vp = pairalloc(ctx, da);
+       vp = fr_pair_afrom_da(ctx, da);
        if (!vp) return NULL;
-       if (pairparsevalue(vp, value, -1) < 0) {
+       if (fr_pair_value_from_str(vp, value, -1) < 0) {
                talloc_free(vp);
                return NULL;
        }
@@ -1164,7 +1226,7 @@ finish:
 }
 
 
-static VALUE_PAIR *pair_unknown2known(VALUE_PAIR *vp, DICT_ATTR const *da)
+static VALUE_PAIR *fr_pair_from_unkown(VALUE_PAIR *vp, DICT_ATTR const *da)
 {
        ssize_t len;
        VALUE_PAIR *vp2;
@@ -1175,7 +1237,7 @@ static VALUE_PAIR *pair_unknown2known(VALUE_PAIR *vp, DICT_ATTR const *da)
        if (len < 0) return vp; /* it's really unknown */
 
        if (vp2->da->flags.is_unknown) {
-               pairfree(&vp2);
+               fr_pair_list_free(&vp2);
                return vp;
        }
 
@@ -1187,12 +1249,12 @@ static VALUE_PAIR *pair_unknown2known(VALUE_PAIR *vp, DICT_ATTR const *da)
         *      and 1 "unknown" is likely preferable.
         */
        if ((size_t) len < vp->vp_length) {
-               pairfree(&vp2);
+               fr_pair_list_free(&vp2);
                return vp;
        }
 
-       pairsteal(talloc_parent(vp), vp2);
-       pairfree(&vp);
+       fr_pair_steal(talloc_parent(vp), vp2);
+       fr_pair_list_free(&vp);
        return vp2;
 }
 
@@ -1210,9 +1272,9 @@ static VALUE_PAIR *pair_unknown2known(VALUE_PAIR *vp, DICT_ATTR const *da)
  * @param op to assign to new valuepair.
  * @return new valuepair or NULL on error.
  */
-static VALUE_PAIR *pairmake_any(TALLOC_CTX *ctx,
-                               char const *attribute, char const *value,
-                               FR_TOKEN op)
+static VALUE_PAIR *fr_pair_make_unknown(TALLOC_CTX *ctx,
+                                       char const *attribute, char const *value,
+                                       FR_TOKEN op)
 {
        VALUE_PAIR      *vp;
        DICT_ATTR const *da;
@@ -1239,7 +1301,7 @@ static VALUE_PAIR *pairmake_any(TALLOC_CTX *ctx,
         *      it.  This next stop also looks the attribute up in the
         *      dictionary, and creates the appropriate type for it.
         */
-       vp = pairalloc(ctx, da);
+       vp = fr_pair_afrom_da(ctx, da);
        if (!vp) {
                dict_attr_free(&da);
                return NULL;
@@ -1267,7 +1329,7 @@ static VALUE_PAIR *pairmake_any(TALLOC_CTX *ctx,
         */
        da = dict_attrbyvalue(vp->da->attr, vp->da->vendor);
        if (da) {
-               return pair_unknown2known(vp, da);
+               return fr_pair_from_unkown(vp, da);
        }
 
        return vp;
@@ -1288,8 +1350,8 @@ static VALUE_PAIR *pairmake_any(TALLOC_CTX *ctx,
  * @param[in] op to assign to new VALUE_PAIR.
  * @return a new VALUE_PAIR.
  */
-VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
-                    char const *attribute, char const *value, FR_TOKEN op)
+VALUE_PAIR *fr_pair_make(TALLOC_CTX *ctx, VALUE_PAIR **vps,
+                       char const *attribute, char const *value, FR_TOKEN op)
 {
        DICT_ATTR const *da;
        VALUE_PAIR      *vp;
@@ -1341,8 +1403,8 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
         */
        da = dict_attrbyname(attrname);
        if (!da) {
-               vp = pairmake_any(ctx, attrname, value, op);
-               if (vp && vps) pairadd(vps, vp);
+               vp = fr_pair_make_unknown(ctx, attrname, value, op);
+               if (vp && vps) fr_pair_add(vps, vp);
                return vp;
        }
 
@@ -1374,7 +1436,7 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
                }
        }
 
-       vp = pairalloc(ctx, da);
+       vp = fr_pair_afrom_da(ctx, da);
        if (!vp) return NULL;
        vp->op = (op == 0) ? T_OP_EQ : op;
        vp->tag = tag;
@@ -1417,10 +1479,10 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
                }
                talloc_free(preg);
 
-               vp = pairmake(ctx, NULL, attribute, NULL, op);
+               vp = fr_pair_make(ctx, NULL, attribute, NULL, op);
                if (!vp) return NULL;
 
-               if (pairmark_xlat(vp, value) < 0) {
+               if (fr_pair_mark_xlat(vp, value) < 0) {
                        talloc_free(vp);
                        return NULL;
                }
@@ -1434,19 +1496,71 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
        }
 
        /*
+        *      We allow this for stupidity, but it's really a bad idea.
+        */
+       if (vp->da->type == PW_TYPE_TLV) {
+               ssize_t len;
+               DICT_ATTR const *unknown;
+               VALUE_PAIR *head = NULL;
+               VALUE_PAIR **tail = &head;
+
+               if (!value) {
+                       talloc_free(vp);
+                       return NULL;
+               }
+
+               unknown = dict_unknown_afrom_fields(vp, vp->da->attr, vp->da->vendor);
+               if (!unknown) {
+                       talloc_free(vp);
+                       return NULL;
+               }
+
+               vp->da = unknown;
+
+               /*
+                *      Parse it as an unknown type, i.e. octets.
+                */
+               if (fr_pair_value_from_str(vp, value, -1) < 0) {
+                       talloc_free(vp);
+                       return NULL;
+               }
+
+               /*
+                *      It's badly formatted.  Treat it as unknown.
+                */
+               if (rad_tlv_ok(vp->vp_octets, vp->vp_length, 1, 1) < 0) {
+                       goto do_add;
+               }
+
+               /*
+                *      Decode the TLVs
+                */
+               len = rad_data2vp_tlvs(ctx, NULL, NULL, NULL, da, vp->vp_octets,
+                                      vp->vp_length, tail);
+               if (len < 0) {
+                       goto do_add;
+               }
+
+               talloc_free(vp);
+               vp = head;
+               goto do_add;
+       }
+
+       /*
         *      FIXME: if (strcasecmp(attribute, vp->da->name) != 0)
         *      then the user MAY have typed in the attribute name
         *      as Vendor-%d-Attr-%d, and the value MAY be octets.
         *
-        *      We probably want to fix pairparsevalue to accept
+        *      We probably want to fix fr_pair_value_from_str to accept
         *      octets as values for any attribute.
         */
-       if (value && (pairparsevalue(vp, value, -1) < 0)) {
+       if (value && (fr_pair_value_from_str(vp, value, -1) < 0)) {
                talloc_free(vp);
                return NULL;
        }
 
-       if (vps) pairadd(vps, vp);
+do_add:
+       if (vps) fr_pair_add(vps, vp);
        return vp;
 }
 
@@ -1459,7 +1573,7 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
  * @param value to expand.
  * @return 0 if marking succeeded or -1 if vp already had a value, or OOM.
  */
-int pairmark_xlat(VALUE_PAIR *vp, char const *value)
+int fr_pair_mark_xlat(VALUE_PAIR *vp, char const *value)
 {
        char *raw;
 
@@ -1491,7 +1605,7 @@ int pairmark_xlat(VALUE_PAIR *vp, char const *value)
  * @param[out] raw The struct to write the raw VALUE_PAIR to.
  * @return the last token read.
  */
-FR_TOKEN pairread(char const **ptr, VALUE_PAIR_RAW *raw)
+FR_TOKEN fr_pair_raw_from_str(char const **ptr, VALUE_PAIR_RAW *raw)
 {
        char const      *p;
        char *q;
@@ -1642,10 +1756,16 @@ FR_TOKEN pairread(char const **ptr, VALUE_PAIR_RAW *raw)
                }
 
                break;
-       default:
-               raw->quote = quote;
 
+       case T_SINGLE_QUOTED_STRING:
+       case T_BACK_QUOTED_STRING:
+       case T_BARE_WORD:
+               raw->quote = quote;
                break;
+
+       default:
+               fr_strerror_printf("Failed to find expected value on right hand side");
+               return T_INVALID;
        }
 
        return ret;
@@ -1663,7 +1783,7 @@ FR_TOKEN pairread(char const **ptr, VALUE_PAIR_RAW *raw)
  * @param list where the parsed VALUE_PAIRs will be appended.
  * @return the last token parsed, or T_INVALID
  */
-FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
+FR_TOKEN fr_pair_list_afrom_str(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
 {
        VALUE_PAIR      *vp, *head, **tail;
        char const      *p;
@@ -1685,7 +1805,7 @@ FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
                raw.l_opand[0] = '\0';
                raw.r_opand[0] = '\0';
 
-               last_token = pairread(&p, &raw);
+               last_token = fr_pair_raw_from_str(&p, &raw);
 
                /*
                 *      JUST a hash.  Don't try to create a VP.
@@ -1698,18 +1818,18 @@ FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
                if (last_token == T_INVALID) break;
 
                if (raw.quote == T_DOUBLE_QUOTED_STRING) {
-                       vp = pairmake(ctx, NULL, raw.l_opand, NULL, raw.op);
+                       vp = fr_pair_make(ctx, NULL, raw.l_opand, NULL, raw.op);
                        if (!vp) {
                                last_token = T_INVALID;
                                break;
                        }
-                       if (pairmark_xlat(vp, raw.r_opand) < 0) {
+                       if (fr_pair_mark_xlat(vp, raw.r_opand) < 0) {
                                talloc_free(vp);
                                last_token = T_INVALID;
                                break;
                        }
                } else {
-                       vp = pairmake(ctx, NULL, raw.l_opand, raw.r_opand, raw.op);
+                       vp = fr_pair_make(ctx, NULL, raw.l_opand, raw.r_opand, raw.op);
                        if (!vp) {
                                last_token = T_INVALID;
                                break;
@@ -1721,9 +1841,9 @@ FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
        } while (*p && (last_token == T_COMMA));
 
        if (last_token == T_INVALID) {
-               pairfree(&head);
+               fr_pair_list_free(&head);
        } else {
-               pairadd(list, head);
+               fr_pair_add(list, head);
        }
 
        /*
@@ -1735,7 +1855,7 @@ FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
 /*
  *     Read valuepairs from the fp up to End-Of-File.
  */
-int readvp2(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone)
+int fr_pair_list_afrom_file(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone)
 {
        char buf[8192];
        FR_TOKEN last_token = T_EOL;
@@ -1767,7 +1887,7 @@ int readvp2(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone)
                 *      Read all of the attributes on the current line.
                 */
                vp = NULL;
-               last_token = userparse(ctx, buf, &vp);
+               last_token = fr_pair_list_afrom_str(ctx, buf, &vp);
                if (!vp) {
                        if (last_token != T_EOL) goto error;
                        break;
@@ -1783,7 +1903,7 @@ int readvp2(TALLOC_CTX *ctx, VALUE_PAIR **out, FILE *fp, bool *pfiledone)
 error:
        *pfiledone = false;
        vp = fr_cursor_first(&cursor);
-       if (vp) pairfree(&vp);
+       if (vp) fr_pair_list_free(&vp);
 
        return -1;
 }
@@ -1800,7 +1920,7 @@ error:
  * @param[in] b the second attribute
  * @return 1 if true, 0 if false, -1 on error.
  */
-int paircmp(VALUE_PAIR *a, VALUE_PAIR *b)
+int fr_pair_cmp(VALUE_PAIR *a, VALUE_PAIR *b)
 {
        if (!a) return -1;
 
@@ -1862,7 +1982,7 @@ int paircmp(VALUE_PAIR *a, VALUE_PAIR *b)
                break;
        }
 
-       return paircmp_op(a->op, b, a);
+       return fr_pair_cmp_op(a->op, b, a);
 }
 
 /** Determine equality of two lists
@@ -1873,7 +1993,7 @@ int paircmp(VALUE_PAIR *a, VALUE_PAIR *b)
  * @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 pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b)
+int fr_pair_list_cmp(VALUE_PAIR *a, VALUE_PAIR *b)
 {
        vp_cursor_t a_cursor, b_cursor;
        VALUE_PAIR *a_p, *b_p;
@@ -1925,13 +2045,12 @@ int pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b)
  *
  * @param vp to fixup.
  */
-static void pairtypeset(VALUE_PAIR *vp)
+static void fr_pair_value_set_type(VALUE_PAIR *vp)
 {
        if (!vp->data.ptr) return;
 
        switch (vp->da->type) {
        case PW_TYPE_OCTETS:
-       case PW_TYPE_TLV:
                talloc_set_type(vp->data.ptr, uint8_t);
                return;
 
@@ -1950,7 +2069,7 @@ static void pairtypeset(VALUE_PAIR *vp)
  * @param[in] src data to copy
  * @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)
+void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t size)
 {
        uint8_t *p = NULL, *q;
 
@@ -1968,7 +2087,7 @@ void pairmemcpy(VALUE_PAIR *vp, uint8_t const *src, size_t size)
        vp->vp_octets = p;
        vp->vp_length = size;
 
-       if (size > 0) pairtypeset(vp);
+       if (size > 0) fr_pair_value_set_type(vp);
 }
 
 /** Reparent an allocated octet buffer to a VALUE_PAIR
@@ -1976,7 +2095,7 @@ 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 const *src)
+void fr_pair_value_memsteal(VALUE_PAIR *vp, uint8_t const *src)
 {
        uint8_t *q;
 
@@ -1988,7 +2107,7 @@ void pairmemsteal(VALUE_PAIR *vp, uint8_t const *src)
        vp->vp_octets = talloc_steal(vp, src);
        vp->type = VT_DATA;
        vp->vp_length = talloc_array_length(vp->vp_strvalue);
-       pairtypeset(vp);
+       fr_pair_value_set_type(vp);
 }
 
 /** Reparent an allocated char buffer to a VALUE_PAIR
@@ -1996,7 +2115,7 @@ void pairmemsteal(VALUE_PAIR *vp, uint8_t const *src)
  * @param[in,out] vp to update
  * @param[in] src buffer to steal.
  */
-void pairstrsteal(VALUE_PAIR *vp, char const *src)
+void fr_pair_value_strsteal(VALUE_PAIR *vp, char const *src)
 {
        uint8_t *q;
 
@@ -2008,7 +2127,7 @@ void pairstrsteal(VALUE_PAIR *vp, char const *src)
        vp->vp_strvalue = talloc_steal(vp, src);
        vp->type = VT_DATA;
        vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
-       pairtypeset(vp);
+       fr_pair_value_set_type(vp);
 }
 
 /** Copy data into an "string" data type.
@@ -2016,7 +2135,7 @@ void pairstrsteal(VALUE_PAIR *vp, char const *src)
  * @param[in,out] vp to update
  * @param[in] src data to copy
  */
-void pairstrcpy(VALUE_PAIR *vp, char const *src)
+void fr_pair_value_strcpy(VALUE_PAIR *vp, char const *src)
 {
        char *p, *q;
 
@@ -2032,7 +2151,7 @@ void pairstrcpy(VALUE_PAIR *vp, char const *src)
        vp->vp_strvalue = p;
        vp->type = VT_DATA;
        vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
-       pairtypeset(vp);
+       fr_pair_value_set_type(vp);
 }
 
 /** Copy data into an "string" data type.
@@ -2044,7 +2163,7 @@ void pairstrcpy(VALUE_PAIR *vp, char const *src)
  * @param[in] src data to copy.
  * @param[in] len of data to copy.
  */
-void pairstrncpy(VALUE_PAIR *vp, char const *src, size_t len)
+void fr_pair_value_bstrncpy(VALUE_PAIR *vp, void const *src, size_t len)
 {
        char *p, *q;
 
@@ -2062,7 +2181,7 @@ void pairstrncpy(VALUE_PAIR *vp, char const *src, size_t len)
        vp->vp_strvalue = p;
        vp->type = VT_DATA;
        vp->vp_length = len;
-       pairtypeset(vp);
+       fr_pair_value_set_type(vp);
 }
 
 /** Print data into an "string" data type.
@@ -2070,7 +2189,7 @@ void pairstrncpy(VALUE_PAIR *vp, char const *src, size_t len)
  * @param[in,out] vp to update
  * @param[in] fmt the format string
  */
-void pairsprintf(VALUE_PAIR *vp, char const *fmt, ...)
+void fr_pair_value_sprintf(VALUE_PAIR *vp, char const *fmt, ...)
 {
        va_list ap;
        char *p, *q;
@@ -2090,6 +2209,176 @@ void pairsprintf(VALUE_PAIR *vp, char const *fmt, ...)
        vp->type = VT_DATA;
 
        vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
-       pairtypeset(vp);
+       fr_pair_value_set_type(vp);
 }
 
+#ifdef WITH_VERIFY_PTR
+/*
+ *     Verify a VALUE_PAIR
+ */
+inline void fr_pair_verify(char const *file, int line, VALUE_PAIR const *vp)
+{
+       if (!vp) {
+               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR pointer was NULL", file, line);
+               fr_assert(0);
+               fr_exit_now(1);
+       }
+
+       (void) talloc_get_type_abort(vp, VALUE_PAIR);
+
+       if (!vp->da) {
+               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR da pointer was NULL", file, line);
+               fr_assert(0);
+               fr_exit_now(1);
+       }
+
+       if (vp->data.ptr) switch (vp->da->type) {
+       case PW_TYPE_OCTETS:
+       {
+               size_t len;
+               TALLOC_CTX *parent;
+
+               if (!talloc_get_type(vp->data.ptr, uint8_t)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
+                                    "uint8_t but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
+                       (void) talloc_get_type_abort(vp->data.ptr, uint8_t);
+               }
+
+               len = talloc_array_length(vp->vp_octets);
+               if (vp->vp_length > len) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
+                                    "uint8_t data buffer length %zu\n", file, line, vp->da->name, vp->vp_length, len);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               parent = talloc_parent(vp->data.ptr);
+               if (parent != vp) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer is not "
+                                    "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
+                                    file, line, vp->da->name,
+                                    vp, parent, parent ? talloc_get_name(parent) : "NULL");
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+       }
+               break;
+
+       case PW_TYPE_STRING:
+       {
+               size_t len;
+               TALLOC_CTX *parent;
+
+               if (!talloc_get_type(vp->data.ptr, char)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
+                                    "char but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
+                       (void) talloc_get_type_abort(vp->data.ptr, char);
+               }
+
+               len = (talloc_array_length(vp->vp_strvalue) - 1);
+               if (vp->vp_length > len) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
+                                    "char buffer length %zu\n", file, line, vp->da->name, vp->vp_length, len);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (vp->vp_strvalue[vp->vp_length] != '\0') {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer not \\0 "
+                                    "terminated\n", file, line, vp->da->name);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               parent = talloc_parent(vp->data.ptr);
+               if (parent != vp) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer is not "
+                                    "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
+                                    file, line, vp->da->name,
+                                    vp, parent, parent ? talloc_get_name(parent) : "NULL");
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+       }
+               break;
+
+       default:
+               break;
+       }
+
+       if (vp->da->flags.is_unknown) {
+               (void) talloc_get_type_abort(vp->da, DICT_ATTR);
+       } else {
+               DICT_ATTR const *da;
+
+               /*
+                *      Attribute may be present with multiple names
+                */
+               da = dict_attrbyname(vp->da->name);
+               if (!da) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR attribute %p \"%s\" (%s) "
+                                    "not found in global dictionary",
+                                    file, line, vp->da, vp->da->name,
+                                    fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (da->type == PW_TYPE_COMBO_IP_ADDR) {
+                       da = dict_attrbytype(vp->da->attr, vp->da->vendor, vp->da->type);
+                       if (!da) {
+                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR attribute %p \"%s\" "
+                                            "variant (%s) not found in global dictionary",
+                                            file, line, vp->da, vp->da->name,
+                                            fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
+                               fr_assert(0);
+                               fr_exit_now(1);
+                       }
+               }
+
+
+               if (da != vp->da) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR "
+                                    "dictionary pointer %p \"%s\" (%s) "
+                                    "and global dictionary pointer %p \"%s\" (%s) differ",
+                                    file, line, vp->da, vp->da->name,
+                                    fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"),
+                                    da, da->name, fr_int2str(dict_attr_types, da->type, "<INVALID>"));
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+       }
+}
+
+/*
+ *     Verify a pair list
+ */
+void fr_pair_list_verify(char const *file, int line, 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_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: Expected VALUE_PAIR \"%s\" to be parented "
+                                    "by %p (%s), instead parented by %p (%s)\n",
+                                    file, line, 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);
+
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+       }
+}
+#endif
index 53b0caa..00ccd52 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This program is 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.
+ *   it under the terms of the GNU General Public License, version 2 of 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
@@ -74,6 +73,30 @@ static int _free_pcap(fr_pcap_t *pcap) {
        return 0;
 }
 
+/** Get data link from pcap_if_t
+ *
+ * libpcap requires an open pcap handle to get data_link type
+ * unfortunately when we're trying to find useful interfaces
+ * this is too late.
+ *
+ * @param errbuff Error message.
+ * @param dev to get link layer for.
+ * @return datalink layer or -1 on failure.
+ */
+int fr_pcap_if_link_layer(char *errbuff, pcap_if_t *dev)
+{
+       pcap_t *pcap;
+       int data_link;
+
+       pcap = pcap_open_live(dev->name, 0, 0, 0, errbuff);
+       if (!pcap) return -1;
+
+       data_link = pcap_datalink(pcap);
+       pcap_close(pcap);
+
+       return data_link;
+}
+
 /** Initialise a pcap handle abstraction
  *
  * @param ctx talloc TALLOC_CTX to allocate handle in.
@@ -91,7 +114,7 @@ fr_pcap_t *fr_pcap_init(TALLOC_CTX *ctx, char const *name, fr_pcap_type_t type)
        talloc_set_destructor(this, _free_pcap);
        this->name = talloc_typed_strdup(this, name);
        this->type = type;
-       this->link_type = -1;
+       this->link_layer = -1;
 
        return this;
 }
@@ -159,7 +182,7 @@ int fr_pcap_open(fr_pcap_t *pcap)
                }
 
                pcap->fd = pcap_get_selectable_fd(pcap->handle);
-               pcap->link_type = pcap_datalink(pcap->handle);
+               pcap->link_layer = pcap_datalink(pcap->handle);
 #ifndef __linux__
                {
                        int value = 1;
@@ -179,14 +202,14 @@ int fr_pcap_open(fr_pcap_t *pcap)
                        return -1;
                }
                pcap->fd = pcap_get_selectable_fd(pcap->handle);
-               pcap->link_type = pcap_datalink(pcap->handle);
+               pcap->link_layer = pcap_datalink(pcap->handle);
                break;
 
        case PCAP_FILE_OUT:
-               if (pcap->link_type < 0) {
-                       pcap->link_type = DLT_EN10MB;
+               if (pcap->link_layer < 0) {
+                       pcap->link_layer = DLT_EN10MB;
                }
-               pcap->handle = pcap_open_dead(pcap->link_type, SNAPLEN);
+               pcap->handle = pcap_open_dead(pcap->link_layer, SNAPLEN);
                if (!pcap->handle) {
                        fr_strerror_printf("Unknown error occurred opening dead PCAP handle");
 
@@ -209,7 +232,7 @@ int fr_pcap_open(fr_pcap_t *pcap)
                        return -1;
                }
                pcap->fd = pcap_get_selectable_fd(pcap->handle);
-               pcap->link_type = pcap_datalink(pcap->handle);
+               pcap->link_layer = pcap_datalink(pcap->handle);
                break;
 #else
        case PCAP_STDIO_IN:
@@ -233,7 +256,7 @@ int fr_pcap_open(fr_pcap_t *pcap)
 
                return -1;
 #endif
-               case PCAP_INVALID:
+       case PCAP_INVALID:
        default:
                fr_assert(0);
                fr_strerror_printf("Bad handle type (%i)", pcap->type);
@@ -264,7 +287,7 @@ int fr_pcap_apply_filter(fr_pcap_t *pcap, char const *expression)
         *      https://github.com/the-tcpdump-group/libpcap/commit/676cf8a61ed240d0a86d471ef419f45ba35dba80
         */
 #ifdef DLT_NFLOG
-       if (pcap->link_type == DLT_NFLOG) {
+       if (pcap->link_layer == DLT_NFLOG) {
                fr_strerror_printf("NFLOG link-layer type filtering not implemented");
 
                return 1;
index 9113809..0d11ef7 100644 (file)
@@ -5,8 +5,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -27,30 +26,35 @@ RCSID("$Id$")
 
 #include       <ctype.h>
 
-/*
- *     Checks for utf-8, taken from:
- *
- *  http://www.w3.org/International/questions/qa-forms-utf-8
+/** Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8
  *
- *     Note that we don't care about the length of the input string,
- *     because '\0' is an invalid UTF-8 character.
+ * @param str input string.
+ * @param inlen length of input string.  May be -1 if str is \0 terminated.
  */
-int fr_utf8_char(uint8_t const *str)
+int fr_utf8_char(uint8_t const *str, ssize_t inlen)
 {
+       if (inlen == 0) return 0;
+
+       if (inlen < 0) inlen = 4;       /* longest char */
+
        if (*str < 0x20) return 0;
 
-       if (*str <= 0x7e) return 1; /* 1 */
+       if (*str <= 0x7e) return 1;     /* 1 */
 
        if (*str <= 0xc1) return 0;
 
-       if ((str[0] >= 0xc2) && /* 2 */
+       if (inlen < 2) return 0;
+
+       if ((str[0] >= 0xc2) &&         /* 2 */
            (str[0] <= 0xdf) &&
            (str[1] >= 0x80) &&
            (str[1] <= 0xbf)) {
                return 2;
        }
 
-       if ((str[0] == 0xe0) && /* 3 */
+       if (inlen < 3) return 0;
+
+       if ((str[0] == 0xe0) &&         /* 3 */
            (str[1] >= 0xa0) &&
            (str[1] <= 0xbf) &&
            (str[2] >= 0x80) &&
@@ -58,7 +62,7 @@ int fr_utf8_char(uint8_t const *str)
                return 3;
        }
 
-       if ((str[0] >= 0xe1) && /* 4a */
+       if ((str[0] >= 0xe1) &&         /* 4a */
            (str[0] <= 0xec) &&
            (str[1] >= 0x80) &&
            (str[1] <= 0xbf) &&
@@ -67,7 +71,7 @@ int fr_utf8_char(uint8_t const *str)
                return 3;
        }
 
-       if ((str[0] >= 0xee) && /* 4b */
+       if ((str[0] >= 0xee) &&         /* 4b */
            (str[0] <= 0xef) &&
            (str[1] >= 0x80) &&
            (str[1] <= 0xbf) &&
@@ -76,7 +80,7 @@ int fr_utf8_char(uint8_t const *str)
                return 3;
        }
 
-       if ((str[0] == 0xed) && /* 5 */
+       if ((str[0] == 0xed) &&         /* 5 */
            (str[1] >= 0x80) &&
            (str[1] <= 0x9f) &&
            (str[2] >= 0x80) &&
@@ -84,7 +88,9 @@ int fr_utf8_char(uint8_t const *str)
                return 3;
        }
 
-       if ((str[0] == 0xf0) && /* 6 */
+       if (inlen < 4) return 0;
+
+       if ((str[0] == 0xf0) &&         /* 6 */
            (str[1] >= 0x90) &&
            (str[1] <= 0xbf) &&
            (str[2] >= 0x80) &&
@@ -94,7 +100,7 @@ int fr_utf8_char(uint8_t const *str)
                return 4;
        }
 
-       if ((str[0] >= 0xf1) && /* 6 */
+       if ((str[0] >= 0xf1) &&         /* 6 */
            (str[1] <= 0xf3) &&
            (str[1] >= 0x80) &&
            (str[1] <= 0xbf) &&
@@ -106,7 +112,7 @@ int fr_utf8_char(uint8_t const *str)
        }
 
 
-       if ((str[0] == 0xf4) && /* 7 */
+       if ((str[0] == 0xf4) &&         /* 7 */
            (str[1] >= 0x80) &&
            (str[1] <= 0x8f) &&
            (str[2] >= 0x80) &&
@@ -133,14 +139,14 @@ char const *fr_utf8_strchr(int *chr_len, char const *str, char const *chr)
 {
        int cchr;
 
-       cchr = fr_utf8_char((uint8_t const *)chr);
+       cchr = fr_utf8_char((uint8_t const *)chr, -1);
        if (cchr == 0) cchr = 1;
        if (chr_len) *chr_len = cchr;
 
        while (*str) {
                int schr;
 
-               schr = fr_utf8_char((uint8_t const *) str);
+               schr = fr_utf8_char((uint8_t const *) str, -1);
                if (schr == 0) schr = 1;
                if (schr != cchr) goto next;
 
@@ -160,52 +166,35 @@ char const *fr_utf8_strchr(int *chr_len, char const *str, char const *chr)
  * @note Will always \0 terminate unless outlen == 0.
  *
  * @param[in] in string to escape.
- * @param[in] inlen length of string to escape (lets us deal with embedded NULLs)
+ * @param[in] inlen length of string to escape (lets us deal with embedded NULs)
  * @param[out] out where to write the escaped string.
  * @param[out] outlen the length of the buffer pointed to by out.
  * @param[in] quote the quotation character
- * @return the number of bytes written to the out buffer, or a number >= outlen if truncation has occurred.
+ * @return the number of bytes it WOULD HAVE written to the buffer, not including the trailing NUL
  */
 size_t fr_prints(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
 {
        uint8_t const   *p = (uint8_t const *) in;
-       int             utf8 = 0;
-       size_t          freespace = outlen;
-
-       /*
-        *      IF YOU MODIFY THIS FUNCTION, YOU MUST MAKE
-        *      EQUIVALENT MODIFICATIONS TO fr_prints_len
-        */
-
-       /* Can't '\0' terminate */
-       if (freespace == 0) return inlen;
+       size_t          utf8;
+       size_t          used;
+       size_t          freespace;
 
        /* No input, so no output... */
        if (!in) {
-       no_input:
-               *out = '\0';
+               if (out && outlen) *out = '\0';
                return 0;
        }
 
        /* 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;
-       }
-
        /*
         *      No quotation character, just use memcpy, ensuring we
         *      don't overflow the output buffer.
         */
        if (!quote) {
+               if (!out) return inlen;
+
                if ((size_t)inlen >= outlen) {
                        memcpy(out, in, outlen - 1);
                        out[outlen - 1] = '\0';
@@ -213,9 +202,20 @@ size_t fr_prints(char *out, size_t outlen, char const *in, ssize_t inlen, char q
                        memcpy(out, in, inlen);
                        out[inlen] = '\0';
                }
+
                return inlen;
        }
 
+       /*
+        *      Check the output buffer and length.  Zero both of them
+        *      out if either are zero.
+        */
+       freespace = outlen;
+       if (freespace == 0) out = NULL;
+       if (!out) freespace = 0;
+
+       used = 0;
+
        while (inlen > 0) {
                int sp = 0;
 
@@ -230,40 +230,28 @@ size_t fr_prints(char *out, size_t outlen, char const *in, ssize_t inlen, char q
                }
 
                /*
-                *      Escape the quotation character, if we have one.
+                *      Always escape the quotation character.
                 */
                if (*p == quote) {
                        sp = quote;
+                       goto do_escape;
+               }
 
-               } else switch (*p) {
-               case '\\':
-                       /*
-                        *      If the next character is a quote, we
-                        *      need to escape the backslash.
-                        */
-                       if (p[1] == quote) {
-                               sp = '\\';
-
-                       /*
-                        *      Ensure that "\r" isn't
-                        *      interpreted as '\r', but
-                        *      instead as "\\r"
-                        */
-                       } else switch (p[1]) {
-                       case 'r':
-                       case 't':
-                       case 'n':
-                       case '\\':
+               /*
+                *      Escape the backslash ONLY for single quoted strings.
+                */
+               if (quote == '\'') {
+                       if (*p == '\\') {
                                sp = '\\';
-                               break;
-
-                       default:
-                               sp = '\0';
-                               break;
-
                        }
-                       break;
+                       goto do_escape;
+               }
 
+               /*
+                *      Try to convert 0x0a --> \r, etc.
+                *      Backslashes get handled specially.
+                */
+               switch (*p) {
                case '\r':
                        sp = 'r';
                        break;
@@ -276,47 +264,93 @@ size_t fr_prints(char *out, size_t outlen, char const *in, ssize_t inlen, char q
                        sp = 't';
                        break;
 
+               case '\\':
+                       sp = '\\';
+                       break;
+
                default:
                        sp = '\0';
                        break;
-               }
+               } /* escape the character at *p */
 
+       do_escape:
                if (sp) {
-                       if (freespace < 3) break; /* \ + <c> + \0 */
-                       *out++ = '\\';
-                       *out++ = sp;
-                       freespace -= 2;
+                       if ((freespace > 0) && (freespace <= 2)) {
+                               if (out) out[used] = '\0';
+                               out = NULL;
+                               freespace = 0;
+
+                       } else if (freespace > 2) { /* room for char AND trailing zero */
+                               if (out) {
+                                       out[used] = '\\';
+                                       out[used + 1] = sp;
+                               }
+                               freespace -= 2;
+                       }
+
+                       used += 2;
                        p++;
                        inlen--;
                        continue;
                }
 
-               utf8 = fr_utf8_char(p);
+               /*
+                *      All strings are UTF-8 clean.
+                */
+               utf8 = fr_utf8_char(p, inlen);
+
+               /*
+                *      If we have an invalid UTF-8 character, it gets
+                *      copied over as a 1-byte character for single
+                *      quoted strings.  Which means that the output
+                *      isn't strictly UTF-8, but oh well...
+                *
+                *      For double quoted strints, the invalid
+                *      characters get escaped as octal encodings.
+                */
                if (utf8 == 0) {
-                       if (freespace < 5) break; /* \ + <o><o><o> + \0 */
-                       snprintf(out, freespace, "\\%03o", *p);
-                       out += 4;
-                       freespace -= 4;
-                       p++;
-                       inlen--;
-                       continue;
+                       if (quote == '\'') {
+                               utf8 = 1;
+
+                       } else {
+                               if ((freespace > 0) && (freespace <= 4)) {
+                                       if (out) out[used] = '\0';
+                                       out = NULL;
+                                       freespace = 0;
+
+                               } else if (freespace > 4) { /* room for char AND trailing zero */
+                                       if (out) snprintf(out + used, freespace, "\\%03o", *p);
+                                       freespace -= 4;
+                               }
+
+                               used += 4;
+                               p++;
+                               inlen--;
+                               continue;
+                       }
                }
 
-               do {
-                       if (freespace < 2) goto finish; /* <c> + \0 */
-                       *out++ = *p++;
-                       freespace--;
-                       inlen--;
-               } while (--utf8 > 0);
-       }
+               if ((freespace > 0) && (freespace <= utf8)) {
+                       if (out) out[used] = '\0';
+                       out = NULL;
+                       freespace = 0;
 
-finish:
-       *out = '\0';
+               } else if (freespace > utf8) { /* room for char AND trailing zero */
+                       memcpy(out + used, p, utf8);
+                       freespace -= utf8;
+               }
 
-       /* Indicate truncation occurred */
-       if (inlen > 0) return outlen + inlen;
+               used += utf8;
+               p += utf8;
+               inlen -= utf8;
+       }
 
-       return outlen - freespace;
+       /*
+        *      Ensure that the output buffer is always zero terminated.
+        */
+       if (out && freespace) out[used] = '\0';
+
+       return used;
 }
 
 /** Find the length of the buffer required to fully escape a string with fr_prints
@@ -328,100 +362,11 @@ finish:
  * @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.
  * @param[in] quote the quotation character.
- * @return the size of buffer required to hold the escaped string including the NULL byte.
+ * @return the size of buffer required to hold the escaped string including the NUL byte.
  */
 size_t fr_prints_len(char const *in, ssize_t inlen, char quote)
 {
-       uint8_t const   *p = (uint8_t const *) in;
-       size_t          outlen = 1;     /* Need one byte for \0 */
-       int             utf8 = 0;
-
-       if (!in) return outlen;
-
-       if (inlen < 0) inlen = strlen(in);
-
-       if (!quote) return inlen + 1;
-
-       while (inlen > 0) {
-               int sp = 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;
-               }
-
-               if (quote && (*p == quote)) {
-                       sp = quote;
-               } else switch (*p) {
-               case '\\':
-                       /*
-                        *      If the next character is a quote, we
-                        *      need to escape the backslash.
-                        */
-                       if (quote && (p[1] == quote)) {
-                               sp = '\\';
-                       /*
-                        *      Ensure that "\r" isn't
-                        *      interpreted as '\r', but
-                        *      instead as "\\r"
-                        */
-                       } else switch (p[1]) {
-                       case 'r':
-                       case 't':
-                       case 'n':
-                       case '\\':
-                               sp = '\\';
-                               break;
-
-                       default:
-                               sp = '\0';
-                               break;
-
-                       }
-                       break;
-
-               case '\r':
-                       sp = 'r';
-                       break;
-
-               case '\n':
-                       sp = 'n';
-                       break;
-
-               case '\t':
-                       sp = 't';
-                       break;
-
-               default:
-                       sp = '\0';
-                       break;
-               }
-
-               if (sp) {
-                       outlen += 2;
-                       p++;
-                       inlen--;
-                       continue;
-               }
-
-               utf8 = fr_utf8_char(p);
-               if (utf8 == 0) {
-                       outlen += 4;
-                       p++;
-                       inlen--;
-                       continue;
-               }
-
-               outlen += utf8;
-               p += utf8;
-               inlen -= utf8;
-       }
-
-       return outlen;
+       return fr_prints(NULL, 0, in, inlen, quote) + 1;
 }
 
 /** Escape string that may contain binary data, and write it to a new buffer
@@ -447,6 +392,7 @@ char *fr_aprints(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
 
        out = talloc_array(ctx, char, len);
        ret = fr_prints(out, len, in, inlen, quote);
+
        /*
         *      This is a fatal error, but fr_assert is the strongest
         *      assert we're allowed to use in library functions.
@@ -461,238 +407,6 @@ char *fr_aprints(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
 
 /** Print the value of an attribute to a string
  *
- * @note return value should be checked with is_truncated.
- * @note Will always \0 terminate unless outlen == 0.
- *
- * @param out Where to write the printed version of the attribute value.
- * @param outlen Length of the output buffer.
- * @param type of data being printed.
- * @param enumv Enumerated string values for integer types.
- * @param data to print.
- * @param inlen Length of data.
- * @param quote char to escape in string output.
- * @return  the number of bytes written to the out buffer, or a number >= outlen if truncation has occurred.
- */
-size_t vp_data_prints_value(char *out, size_t outlen,
-                           PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
-                           ssize_t inlen, char quote)
-{
-       DICT_VALUE      *v;
-       char            buf[1024];      /* Interim buffer to use with poorly behaved printing functions */
-       char const      *a = NULL;
-       time_t          t;
-       struct tm       s_tm;
-       unsigned int    i;
-
-       size_t          len = 0, freespace = outlen;
-
-       if (!data) return 0;
-       if (outlen == 0) return inlen;
-
-       *out = '\0';
-
-       switch (type) {
-       case PW_TYPE_STRING:
-
-               /*
-                *      Ensure that WE add the quotation marks around the string.
-                */
-               if (quote) {
-                       if (freespace < 3) return inlen + 2;
-
-                       *out++ = quote;
-                       freespace--;
-
-                       len = fr_prints(out, freespace, data->strvalue, inlen, quote);
-                       /* always terminate the quoted string with another quote */
-                       if (len >= (freespace - 1)) {
-                               out[outlen - 2] = (char) quote;
-                               out[outlen - 1] = '\0';
-                               return len + 2;
-                       }
-                       out += len;
-                       freespace -= len;
-
-                       *out++ = (char) quote;
-                       freespace--;
-                       *out = '\0';
-
-                       return len + 2;
-               }
-
-               return fr_prints(out, outlen, data->strvalue, inlen, quote);
-
-       case PW_TYPE_INTEGER:
-               i = data->integer;
-               goto print_int;
-
-       case PW_TYPE_SHORT:
-               i = data->ushort;
-               goto print_int;
-
-       case PW_TYPE_BYTE:
-               i = data->byte;
-
-print_int:
-               /* Normal, non-tagged attribute */
-               if (enumv && (v = dict_valbyattr(enumv->attr, enumv->vendor, i)) != NULL) {
-                       a = v->name;
-                       len = strlen(a);
-               } else {
-                       /* should never be truncated */
-                       len = snprintf(buf, sizeof(buf), "%u", i);
-                       a = buf;
-               }
-               break;
-
-       case PW_TYPE_INTEGER64:
-               return snprintf(out, outlen, "%" PRIu64, data->integer64);
-
-       case PW_TYPE_DATE:
-               t = data->date;
-               if (quote > 0) {
-                       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));
-               }
-               a = buf;
-               break;
-
-       case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
-               len = snprintf(buf, sizeof(buf), "%d", data->sinteger);
-               a = buf;
-               break;
-
-       case PW_TYPE_IPV4_ADDR:
-               a = inet_ntop(AF_INET, &(data->ipaddr), buf, sizeof(buf));
-               len = strlen(buf);
-               break;
-
-       case PW_TYPE_ABINARY:
-#ifdef WITH_ASCEND_BINARY
-               print_abinary(buf, sizeof(buf), (uint8_t const *) data->filter, len, quote);
-               a = buf;
-               len = strlen(buf);
-               break;
-#else
-       /* FALL THROUGH */
-#endif
-       case PW_TYPE_OCTETS:
-       case PW_TYPE_TLV:
-       {
-               size_t max;
-
-               /* Return the number of bytes we would have written */
-               len = (inlen * 2) + 2;
-               if (freespace <= 1) {
-                       return len;
-               }
-
-               *out++ = '0';
-               freespace--;
-
-               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, data->octets, ((size_t)inlen > max) ? max : (size_t)inlen);
-       }
-               return len;
-
-       case PW_TYPE_IFID:
-               a = ifid_ntoa(buf, sizeof(buf), data->ifid);
-               len = strlen(buf);
-               break;
-
-       case PW_TYPE_IPV6_ADDR:
-               a = inet_ntop(AF_INET6, &data->ipv6addr, buf, sizeof(buf));
-               len = strlen(buf);
-               break;
-
-       case PW_TYPE_IPV6_PREFIX:
-       {
-               struct in6_addr addr;
-
-               /*
-                *      Alignment issues.
-                */
-               memcpy(&addr, &(data->ipv6prefix[2]), sizeof(addr));
-
-               a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
-               if (a) {
-                       char *p = buf;
-
-                       len = strlen(buf);
-                       p += len;
-                       len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) data->ipv6prefix[1]);
-               }
-       }
-               break;
-
-       case PW_TYPE_IPV4_PREFIX:
-       {
-               struct in_addr addr;
-
-               /*
-                *      Alignment issues.
-                */
-               memcpy(&addr, &(data->ipv4prefix[2]), sizeof(addr));
-
-               a = inet_ntop(AF_INET, &addr, buf, sizeof(buf));
-               if (a) {
-                       char *p = buf;
-
-                       len = strlen(buf);
-                       p += len;
-                       len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) (data->ipv4prefix[1] & 0x3f));
-               }
-       }
-               break;
-
-       case PW_TYPE_ETHERNET:
-               return snprintf(out, outlen, "%02x:%02x:%02x:%02x:%02x:%02x",
-                               data->ether[0], data->ether[1],
-                               data->ether[2], data->ether[3],
-                               data->ether[4], data->ether[5]);
-
-       /*
-        *      Don't add default here
-        */
-       case PW_TYPE_INVALID:
-       case PW_TYPE_COMBO_IP_ADDR:
-       case PW_TYPE_COMBO_IP_PREFIX:
-       case PW_TYPE_EXTENDED:
-       case PW_TYPE_LONG_EXTENDED:
-       case PW_TYPE_EVS:
-       case PW_TYPE_VSA:
-       case PW_TYPE_TIMEVAL:
-       case PW_TYPE_BOOLEAN:
-       case PW_TYPE_MAX:
-               fr_assert(0);
-               *out = '\0';
-               return 0;
-       }
-
-       if (a) strlcpy(out, a, outlen);
-
-       return len;     /* Return the number of bytes we would of written (for truncation detection) */
-}
-
-/** Print the value of an attribute to a string
- *
  * @param[out] out Where to write the string.
  * @param[in] outlen Size of outlen (must be at least 3 bytes).
  * @param[in] vp to print.
@@ -704,176 +418,11 @@ size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, char quot
 {
        VERIFY_VP(vp);
 
-       return vp_data_prints_value(out, outlen, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
-}
-
-
-/** Print one attribute value to a string
- *
- */
-char *vp_data_aprints_value(TALLOC_CTX *ctx,
-                           PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
-                           size_t inlen, char quote)
-{
-       char *p = NULL;
-       unsigned int i;
-
-       switch (type) {
-       case PW_TYPE_STRING:
-       {
-               size_t len, ret;
-
-               if (!quote) {
-                       p = talloc_memdup(ctx, data->strvalue, inlen + 1);
-                       if (!p) return NULL;
-                       talloc_set_type(p, char);
-                       return p;
-               }
-
-               /* Gets us the size of the buffer we need to alloc */
-               len = fr_prints_len(data->strvalue, inlen, quote);
-               p = talloc_array(ctx, char, len);
-               if (!p) return NULL;
-
-               ret = fr_prints(p, len, data->strvalue, inlen, quote);
-               if (!fr_assert(ret == (len - 1))) {
-                       talloc_free(p);
-                       return NULL;
-               }
-               break;
-       }
-
-       case PW_TYPE_INTEGER:
-               i = data->integer;
-               goto print_int;
-
-       case PW_TYPE_SHORT:
-               i = data->ushort;
-               goto print_int;
-
-       case PW_TYPE_BYTE:
-               i = data->byte;
-
-       print_int:
-       {
-               DICT_VALUE const *dv;
-
-               if (enumv && (dv = dict_valbyattr(enumv->attr, enumv->vendor, i))) {
-                       p = talloc_typed_strdup(ctx, dv->name);
-               } else {
-                       p = talloc_typed_asprintf(ctx, "%u", i);
-               }
-       }
-               break;
-
-       case PW_TYPE_SIGNED:
-               p = talloc_typed_asprintf(ctx, "%d", data->sinteger);
-               break;
-
-       case PW_TYPE_INTEGER64:
-               p = talloc_typed_asprintf(ctx, "%" PRIu64 , data->integer64);
-               break;
-
-       case PW_TYPE_ETHERNET:
-               p = talloc_typed_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
-                                         data->ether[0], data->ether[1],
-                                         data->ether[2], data->ether[3],
-                                         data->ether[4], data->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, (uint8_t *) &data->filter, inlen, 0);
-               break;
-#else
-                 /* FALL THROUGH */
-#endif
-
-       case PW_TYPE_OCTETS:
-               p = talloc_array(ctx, char, 2 + 1 + inlen * 2);
-               if (!p) return NULL;
-               p[0] = '0';
-               p[1] = 'x';
-
-               fr_bin2hex(p + 2, data->octets, inlen);
-               break;
-
-       case PW_TYPE_DATE:
-       {
-               time_t t;
-               struct tm s_tm;
-
-               t = data->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_data_prints_value(buff, sizeof(buff), type, enumv, data, inlen, '\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_data_prints_value(buff, sizeof(buff), type, enumv, data, inlen, '\0');
-
-               p = talloc_typed_strdup(ctx, buff);
-       }
-       break;
-
-       case PW_TYPE_IFID:
-               p = talloc_typed_asprintf(ctx, "%x:%x:%x:%x",
-                                         (data->ifid[0] << 8) | data->ifid[1],
-                                         (data->ifid[2] << 8) | data->ifid[3],
-                                         (data->ifid[4] << 8) | data->ifid[5],
-                                         (data->ifid[6] << 8) | data->ifid[7]);
-               break;
-
-       case PW_TYPE_BOOLEAN:
-               p = talloc_typed_strdup(ctx, data->byte ? "yes" : "no");
-               break;
-
-       /*
-        *      Don't add default here
-        */
-       case PW_TYPE_INVALID:
-       case PW_TYPE_COMBO_IP_ADDR:
-       case PW_TYPE_COMBO_IP_PREFIX:
-       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);
-               return NULL;
+       if (vp->type == VT_XLAT) {
+               return snprintf(out, outlen, "%c%s%c", quote, vp->value.xlat, quote);
        }
 
-       return p;
+       return value_data_prints(out, outlen, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
 }
 
 /** Print one attribute value to a string
@@ -887,7 +436,7 @@ char *vp_aprints_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
 {
        VERIFY_VP(vp);
 
-       return vp_data_aprints_value(ctx, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
+       return value_data_aprints(ctx, vp->da->type, vp->da, &vp->data, vp->vp_length, quote);
 }
 
 char *vp_aprints_type(TALLOC_CTX *ctx, PW_TYPE type)
@@ -1131,7 +680,7 @@ size_t vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp)
        out += len;
        freespace -= len;
 
-       len = vp_prints_value(out, freespace, vp, '\'');
+       len = vp_prints_value(out, freespace, vp, '"');
        if (is_truncated(len, freespace)) return (outlen - freespace) + len;
        freespace -= len;
 
@@ -1178,11 +727,15 @@ void vp_print(FILE *fp, VALUE_PAIR const *vp)
 /** Print a list of attributes and enumv
  *
  * @param fp to output to.
- * @param vp to print.
+ * @param const_vp to print.
  */
-void vp_printlist(FILE *fp, VALUE_PAIR const *vp)
+void vp_printlist(FILE *fp, VALUE_PAIR const *const_vp)
 {
+       VALUE_PAIR *vp;
        vp_cursor_t cursor;
+
+       memcpy(&vp, &const_vp, sizeof(vp)); /* const work-arounds */
+
        for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) {
                vp_print(fp, vp);
        }
@@ -1236,5 +789,3 @@ char *vp_aprints(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
 
        return str;
 }
-
-
index f7400f4..7664cf9 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -37,6 +36,11 @@ RCSID("$Id$")
 #include       <freeradius-devel/udpfromto.h>
 #endif
 
+/*
+ *     Some messages get printed out only in debugging mode.
+ */
+#define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf
+
 #if 0
 #define VP_TRACE printf
 
@@ -153,7 +157,7 @@ void fr_printf_log(char const *fmt, ...)
        va_list ap;
 
        va_start(ap, fmt);
-       if ((fr_debug_flag == 0) || !fr_log_fp) {
+       if ((fr_debug_lvl == 0) || !fr_log_fp) {
                va_end(ap);
                return;
        }
@@ -186,7 +190,32 @@ void rad_print_hex(RADIUS_PACKET *packet)
 
        if (!packet->data || !fr_log_fp) return;
 
-       fprintf(fr_log_fp, "  Code:\t\t%u\n", packet->data[0]);
+       fprintf(fr_log_fp, "  Socket:\t%d\n", packet->sockfd);
+#ifdef WITH_TCP
+       fprintf(fr_log_fp, "  Proto:\t%d\n", packet->proto);
+#endif
+
+       if (packet->src_ipaddr.af == AF_INET) {
+               char buffer[32];
+
+               fprintf(fr_log_fp, "  Src IP:\t%s\n",
+                       inet_ntop(packet->src_ipaddr.af,
+                                 &packet->src_ipaddr.ipaddr,
+                                 buffer, sizeof(buffer)));
+               fprintf(fr_log_fp, "    port:\t%u\n", packet->src_port);
+
+               fprintf(fr_log_fp, "  Dst IP:\t%s\n",
+                       inet_ntop(packet->dst_ipaddr.af,
+                                 &packet->dst_ipaddr.ipaddr,
+                                 buffer, sizeof(buffer)));
+               fprintf(fr_log_fp, "    port:\t%u\n", packet->dst_port);
+       }
+
+       if (packet->data[0] < FR_MAX_PACKET_CODE) {
+               fprintf(fr_log_fp, "  Code:\t\t(%d) %s\n", packet->data[0], fr_packet_codes[packet->data[0]]);
+       } else {
+               fprintf(fr_log_fp, "  Code:\t\t%u\n", packet->data[0]);
+       }
        fprintf(fr_log_fp, "  Id:\t\t%u\n", packet->data[1]);
        fprintf(fr_log_fp, "  Length:\t%u\n", ((packet->data[2] << 8) |
                                   (packet->data[3])));
@@ -314,7 +343,20 @@ void rad_recv_discard(int sockfd)
                        (struct sockaddr *)&src, &sizeof_src);
 }
 
-
+/** Basic validation of RADIUS packet header
+ *
+ * @note fr_strerror errors are only available if fr_debug_lvl > 0. This is to reduce CPU time
+ *     consumed when discarding malformed packet.
+ *
+ * @param[in] sockfd we're reading from.
+ * @param[out] src_ipaddr of the packet.
+ * @param[out] src_port of the packet.
+ * @param[out] code Pointer to where to write the packet code.
+ * @return
+ *     - -1 on failure.
+ *     - 1 on decode error.
+ *     - >= RADIUS_HDR_LEN on success. This is the packet length as specified in the header.
+ */
 ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code)
 {
        ssize_t                 data_len, packet_len;
@@ -322,54 +364,59 @@ ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port,
        struct sockaddr_storage src;
        socklen_t               sizeof_src = sizeof(src);
 
-       data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK,
-                           (struct sockaddr *)&src, &sizeof_src);
+       data_len = recvfrom(sockfd, header, sizeof(header), MSG_PEEK, (struct sockaddr *)&src, &sizeof_src);
        if (data_len < 0) {
                if ((errno == EAGAIN) || (errno == EINTR)) return 0;
                return -1;
        }
 
        /*
-        *      Too little data is available, discard the packet.
+        *      Convert AF.  If unknown, discard packet.
         */
-       if (data_len < 4) {
+       if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
+               FR_DEBUG_STRERROR_PRINTF("Unknown address family");
                rad_recv_discard(sockfd);
 
                return 1;
+       }
 
-       } else {                /* we got 4 bytes of data. */
-               /*
-                *      See how long the packet says it is.
-                */
-               packet_len = (header[2] * 256) + header[3];
-
-               /*
-                *      The length in the packet says it's less than
-                *      a RADIUS header length: discard it.
-                */
-               if (packet_len < RADIUS_HDR_LEN) {
-                       rad_recv_discard(sockfd);
+       /*
+        *      Too little data is available, discard the packet.
+        */
+       if (data_len < 4) {
+               FR_DEBUG_STRERROR_PRINTF("Expected at least 4 bytes of header data, got %zu bytes", data_len);
+invalid:
+               FR_DEBUG_STRERROR_PRINTF("Invalid data from %s: %s",
+                                        fr_inet_ntop(src_ipaddr->af, &src_ipaddr->ipaddr),
+                                        fr_strerror());
+               rad_recv_discard(sockfd);
 
-                       return 1;
+               return 1;
+       }
 
-                       /*
-                        *      Enforce RFC requirements, for sanity.
-                        *      Anything after 4k will be discarded.
-                        */
-               } else if (packet_len > MAX_PACKET_LEN) {
-                       rad_recv_discard(sockfd);
+       /*
+        *      See how long the packet says it is.
+        */
+       packet_len = (header[2] * 256) + header[3];
 
-                       return 1;
-               }
+       /*
+        *      The length in the packet says it's less than
+        *      a RADIUS header length: discard it.
+        */
+       if (packet_len < RADIUS_HDR_LEN) {
+               FR_DEBUG_STRERROR_PRINTF("Expected at least " STRINGIFY(RADIUS_HDR_LEN)  " bytes of packet "
+                                        "data, got %zu bytes", packet_len);
+               goto invalid;
        }
 
        /*
-        *      Convert AF.  If unknown, discard packet.
+        *      Enforce RFC requirements, for sanity.
+        *      Anything after 4k will be discarded.
         */
-       if (!fr_sockaddr2ipaddr(&src, sizeof_src, src_ipaddr, src_port)) {
-               rad_recv_discard(sockfd);
-
-               return 1;
+       if (packet_len > MAX_PACKET_LEN) {
+               FR_DEBUG_STRERROR_PRINTF("Length field value too large, expected maximum of "
+                                        STRINGIFY(MAX_PACKET_LEN) " bytes, got %zu bytes", packet_len);
+               goto invalid;
        }
 
        *code = header[0];
@@ -572,62 +619,59 @@ static void make_passwd(uint8_t *output, ssize_t *outlen,
        memcpy(output, passwd, len);
 }
 
+
 static void make_tunnel_passwd(uint8_t *output, ssize_t *outlen,
                               uint8_t const *input, size_t inlen, size_t room,
                               char const *secret, uint8_t const *vector)
 {
        FR_MD5_CTX context, old;
        uint8_t digest[AUTH_VECTOR_LEN];
-       uint8_t passwd[MAX_STRING_LEN + AUTH_VECTOR_LEN];
-       int     i, n;
-       int     len;
+       size_t  i, n;
+       size_t  encrypted_len;
 
        /*
-        *      Be paranoid.
+        *      The password gets encoded with a 1-byte "length"
+        *      field.  Ensure that it doesn't overflow.
         */
        if (room > 253) room = 253;
 
        /*
-        *      Account for 2 bytes of the salt, and round the room
-        *      available down to the nearest multiple of 16.  Then,
-        *      subtract one from that to account for the length byte,
-        *      and the resulting number is the upper bound on the data
-        *      to copy.
-        *
-        *      We could short-cut this calculation just be forcing
-        *      inlen to be no more than 239.  It would work for all
-        *      VSA's, as we don't pack multiple VSA's into one
-        *      attribute.
-        *
-        *      However, this calculation is more general, if a little
-        *      complex.  And it will work in the future for all possible
-        *      kinds of weird attribute packing.
+        *      Limit the maximum size of the input password.  2 bytes
+        *      are taken up by the salt, and one by the encoded
+        *      "length" field.  Note that if we have a tag, the
+        *      "room" will be 252 octets, not 253 octets.
         */
-       room -= 2;
-       room -= (room & 0x0f);
-       room--;
-
-       if (inlen > room) inlen = room;
+       if (inlen > (room - 3)) inlen = room - 3;
 
        /*
-        *      Length of the encrypted data is password length plus
-        *      one byte for the length of the password.
+        *      Length of the encrypted data is the clear-text
+        *      password length plus one byte which encodes the length
+        *      of the password.  We round up to the nearest encoding
+        *      block.  Note that this can result in the encoding
+        *      length being more than 253 octets.
         */
-       len = inlen + 1;
-       if ((len & 0x0f) != 0) {
-               len += 0x0f;
-               len &= ~0x0f;
+       encrypted_len = inlen + 1;
+       if ((encrypted_len & 0x0f) != 0) {
+               encrypted_len += 0x0f;
+               encrypted_len &= ~0x0f;
        }
-       *outlen = len + 2;      /* account for the salt */
 
        /*
-        *      Copy the password over.
+        *      We need 2 octets for the salt, followed by the actual
+        *      encrypted data.
         */
-       memcpy(passwd + 3, input, inlen);
-       memset(passwd + 3 + inlen, 0, sizeof(passwd) - 3 - inlen);
+       if (encrypted_len > (room - 2)) encrypted_len = room - 2;
+
+       *outlen = encrypted_len + 2;    /* account for the salt */
 
        /*
-        *      Generate salt.  The RFC's say:
+        *      Copy the password over, and zero-fill the remainder.
+        */
+       memcpy(output + 3, input, inlen);
+       memset(output + 3 + inlen, 0, *outlen - 3 - inlen);
+
+       /*
+        *      Generate salt.  The RFCs say:
         *
         *      The high bit of salt[0] must be set, each salt in a
         *      packet should be unique, and they should be random
@@ -635,33 +679,40 @@ static void make_tunnel_passwd(uint8_t *output, ssize_t *outlen,
         *      So, we set the high bit, add in a counter, and then
         *      add in some CSPRNG data.  should be OK..
         */
-       passwd[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
+       output[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) |
                     (fr_rand() & 0x07));
-       passwd[1] = fr_rand();
-       passwd[2] = inlen;      /* length of the password string */
+       output[1] = fr_rand();
+       output[2] = inlen;      /* length of the password string */
 
        fr_md5_init(&context);
        fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
        old = context;
 
        fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
-       fr_md5_update(&context, &passwd[0], 2);
+       fr_md5_update(&context, &output[0], 2);
+
+       for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) {
+               size_t block_len;
 
-       for (n = 0; n < len; n += AUTH_PASS_LEN) {
                if (n > 0) {
                        context = old;
                        fr_md5_update(&context,
-                                      passwd + 2 + n - AUTH_PASS_LEN,
+                                      output + 2 + n - AUTH_PASS_LEN,
                                       AUTH_PASS_LEN);
                }
 
                fr_md5_final(digest, &context);
 
-               for (i = 0; i < AUTH_PASS_LEN; i++) {
-                       passwd[i + 2 + n] ^= digest[i];
+               if ((2 + n + AUTH_PASS_LEN) < room) {
+                       block_len = AUTH_PASS_LEN;
+               } else {
+                       block_len = room - 2 - n;
+               }
+
+               for (i = 0; i < block_len; i++) {
+                       output[i + 2 + n] ^= digest[i];
                }
        }
-       memcpy(output, passwd, len + 2);
 }
 
 static int do_next_tlv(VALUE_PAIR const *vp, VALUE_PAIR const *next, int nest)
@@ -762,7 +813,7 @@ static ssize_t vp2data_tlvs(RADIUS_PACKET const *packet,
                ptr[1] += len;
 
 #ifndef NDEBUG
-               if ((fr_debug_flag > 3) && fr_log_fp) {
+               if ((fr_debug_lvl > 3) && fr_log_fp) {
                        fprintf(fr_log_fp, "\t\t%02x %02x  ", ptr[0], ptr[1]);
                        print_hex_data(ptr + 2, len, 3);
                }
@@ -776,7 +827,7 @@ static ssize_t vp2data_tlvs(RADIUS_PACKET const *packet,
        }
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) {
+       if ((fr_debug_lvl > 3) && fr_log_fp) {
                DICT_ATTR const *da;
 
                da = dict_attrbyvalue(svp->da->attr & ((1 << fr_attr_shift[nest ]) - 1), svp->da->vendor);
@@ -829,7 +880,6 @@ static ssize_t vp2data_any(RADIUS_PACKET const *packet,
        switch (vp->da->type) {
        case PW_TYPE_STRING:
        case PW_TYPE_OCTETS:
-       case PW_TYPE_TLV:
                data = vp->data.ptr;
                if (!data) {
                        fr_strerror_printf("ERROR: Cannot encode NULL data");
@@ -950,13 +1000,15 @@ static ssize_t vp2data_any(RADIUS_PACKET const *packet,
                        make_tunnel_passwd(ptr + lvalue, &len, data, len,
                                           room - lvalue,
                                           secret, original->vector);
+                       len += lvalue;
                        break;
                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,
+                       make_tunnel_passwd(ptr + 1, &len, data, len, room - 1,
                                           secret, packet->vector);
+                       len += lvalue;
                        break;
                }
                break;
@@ -1134,7 +1186,7 @@ int rad_vp2extended(RADIUS_PACKET const *packet,
        ptr[1] += len;
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) {
+       if ((fr_debug_lvl > 3) && fr_log_fp) {
                int jump = 3;
 
                fprintf(fr_log_fp, "\t\t%02x %02x  ", ptr[0], ptr[1]);
@@ -1227,7 +1279,7 @@ int rad_vp2wimax(RADIUS_PACKET const *packet,
        ptr[7] += len;
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) {
+       if ((fr_debug_lvl > 3) && fr_log_fp) {
                fprintf(fr_log_fp, "\t\t%02x %02x  %02x%02x%02x%02x (%u)  %02x %02x %02x   ",
                       ptr[0], ptr[1],
                       ptr[2], ptr[3], ptr[4], ptr[5],
@@ -1277,7 +1329,7 @@ static ssize_t vp2attr_concat(UNUSED RADIUS_PACKET const *packet,
                memcpy(ptr + 2, p, left);
 
 #ifndef NDEBUG
-               if ((fr_debug_flag > 3) && fr_log_fp) {
+               if ((fr_debug_lvl > 3) && fr_log_fp) {
                        fprintf(fr_log_fp, "\t\t%02x %02x  ", ptr[0], ptr[1]);
                        print_hex_data(ptr + 2, len, 3);
                }
@@ -1319,7 +1371,7 @@ static ssize_t vp2attr_rfc(RADIUS_PACKET const *packet,
        ptr[1] += len;
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) {
+       if ((fr_debug_lvl > 3) && fr_log_fp) {
                fprintf(fr_log_fp, "\t\t%02x %02x  ", ptr[0], ptr[1]);
                print_hex_data(ptr + 2, len, 3);
        }
@@ -1409,25 +1461,25 @@ static ssize_t vp2attr_vsa(RADIUS_PACKET const *packet,
        if (dv->length) ptr[dv->type + dv->length - 1] += len;
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) {
+       if ((fr_debug_lvl > 3) && fr_log_fp) {
                switch (dv->type) {
                default:
                        break;
 
                case 4:
-                       if ((fr_debug_flag > 3) && fr_log_fp)
+                       if ((fr_debug_lvl > 3) && fr_log_fp)
                                fprintf(fr_log_fp, "\t\t%02x%02x%02x%02x ",
                                        ptr[0], ptr[1], ptr[2], ptr[3]);
                        break;
 
                case 2:
-                       if ((fr_debug_flag > 3) && fr_log_fp)
+                       if ((fr_debug_lvl > 3) && fr_log_fp)
                                fprintf(fr_log_fp, "\t\t%02x%02x ",
                                        ptr[0], ptr[1]);
                break;
 
                case 1:
-                       if ((fr_debug_flag > 3) && fr_log_fp)
+                       if ((fr_debug_lvl > 3) && fr_log_fp)
                                fprintf(fr_log_fp, "\t\t%02x ", ptr[0]);
                        break;
                }
@@ -1511,7 +1563,7 @@ int rad_vp2vsa(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
        if (len < 0) return len;
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) {
+       if ((fr_debug_lvl > 3) && fr_log_fp) {
                fprintf(fr_log_fp, "\t\t%02x %02x  %02x%02x%02x%02x (%u)  ",
                       ptr[0], ptr[1],
                       ptr[2], ptr[3], ptr[4], ptr[5],
@@ -1564,14 +1616,14 @@ int rad_vp2rfc(RADIUS_PACKET const *packet,
        /*
         *      Message-Authenticator is hard-coded.
         */
-       if (vp->da->attr == PW_MESSAGE_AUTHENTICATOR) {
+       if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) {
                if (room < 18) return -1;
 
                ptr[0] = PW_MESSAGE_AUTHENTICATOR;
                ptr[1] = 18;
                memset(ptr + 2, 0, 16);
 #ifndef NDEBUG
-               if ((fr_debug_flag > 3) && fr_log_fp) {
+               if ((fr_debug_lvl > 3) && fr_log_fp) {
                        fprintf(fr_log_fp, "\t\t50 12 ...\n");
                }
 #endif
@@ -1795,7 +1847,7 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                 *      Set the Message-Authenticator to the correct
                 *      length and initial value.
                 */
-               if (reply->da->attr == PW_MESSAGE_AUTHENTICATOR) {
+               if (!reply->da->vendor && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) {
                        /*
                         *      Cache the offset to the
                         *      Message-Authenticator
@@ -2012,7 +2064,7 @@ int rad_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
        }
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+       if ((fr_debug_lvl > 3) && fr_log_fp) rad_print_hex(packet);
 #endif
 
 #ifdef WITH_TCP
@@ -2261,7 +2313,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
         *      "The minimum length is 20 ..."
         */
        if (packet->data_len < RADIUS_HDR_LEN) {
-               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (received %zu < minimum %d)",
+               FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: too short (received %zu < minimum %d)",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -2285,7 +2337,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
         */
        if ((hdr->code == 0) ||
            (hdr->code >= FR_MAX_PACKET_CODE)) {
-               fr_strerror_printf("WARNING: Bad RADIUS packet from host %s: unknown packet code %d",
+               FR_DEBUG_STRERROR_PRINTF("Bad RADIUS packet from host %s: unknown packet code %d",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -2317,7 +2369,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
         *      "The minimum length is 20 ..."
         */
        if (totallen < RADIUS_HDR_LEN) {
-               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: too short (length %zu < minimum %d)",
+               FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: too short (length %zu < minimum %d)",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -2350,7 +2402,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
         *      i.e. No response to the NAS.
         */
        if (packet->data_len < totallen) {
-               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: received %zu octets, packet length says %zu",
+               FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: received %zu octets, packet length says %zu",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -2396,7 +2448,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
                 *      attribute header.
                 */
                if (count < 2) {
-                       fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute header overflows the packet",
+                       FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: attribute header overflows the packet",
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
@@ -2408,7 +2460,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
                 *      Attribute number zero is NOT defined.
                 */
                if (attr[0] == 0) {
-                       fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0",
+                       FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: Invalid attribute 0",
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
@@ -2421,7 +2473,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
                 *      fields.  Anything shorter is an invalid attribute.
                 */
                if (attr[1] < 2) {
-                       fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u too short",
+                       FR_DEBUG_STRERROR_PRINTF("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)),
@@ -2435,7 +2487,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
                 *      attribute, it's a bad packet.
                 */
                if (count < attr[1]) {
-                       fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u data overflows the packet",
+                       FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: attribute %u data overflows the packet",
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
@@ -2461,7 +2513,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
 
                case PW_MESSAGE_AUTHENTICATOR:
                        if (attr[1] != 2 + AUTH_VECTOR_LEN) {
-                               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
+                               FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d",
                                           inet_ntop(packet->src_ipaddr.af,
                                                     &packet->src_ipaddr.ipaddr,
                                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -2490,7 +2542,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
         *      If not, we complain, and throw the packet away.
         */
        if (count != 0) {
-               fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
+               FR_DEBUG_STRERROR_PRINTF("Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
@@ -2505,7 +2557,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
         */
        if ((fr_max_attributes > 0) &&
            (num_attributes > fr_max_attributes)) {
-               fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+               FR_DEBUG_STRERROR_PRINTF("Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
@@ -2526,7 +2578,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
         *      Message-Authenticator attributes.
         */
        if (require_ma && !seen_ma) {
-               fr_strerror_printf("WARNING: Insecure packet from host %s:  Packet does not contain required Message-Authenticator attribute",
+               FR_DEBUG_STRERROR_PRINTF("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)));
@@ -2582,7 +2634,7 @@ RADIUS_PACKET *rad_recv(TALLOC_CTX *ctx, int fd, int flags)
         *      Check for socket errors.
         */
        if (data_len < 0) {
-               fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
+               FR_DEBUG_STRERROR_PRINTF("Error receiving packet: %s", fr_syserror(errno));
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -2595,7 +2647,7 @@ RADIUS_PACKET *rad_recv(TALLOC_CTX *ctx, 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_DEBUG_STRERROR_PRINTF("Discarding packet: Larger than RFC limitation of 4096 bytes");
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -2608,7 +2660,7 @@ RADIUS_PACKET *rad_recv(TALLOC_CTX *ctx, int fd, int flags)
         *      packet->data == NULL
         */
        if ((packet->data_len == 0) || !packet->data) {
-               fr_strerror_printf("Empty packet: Socket is not ready");
+               FR_DEBUG_STRERROR_PRINTF("Empty packet: Socket is not ready");
                rad_free(&packet);
                return NULL;
        }
@@ -2638,7 +2690,7 @@ RADIUS_PACKET *rad_recv(TALLOC_CTX *ctx, int fd, int flags)
        packet->vps = NULL;
 
 #ifndef NDEBUG
-       if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
+       if ((fr_debug_lvl > 3) && fr_log_fp) rad_print_hex(packet);
 #endif
 
        return packet;
@@ -2852,13 +2904,13 @@ static ssize_t data2vp_concat(TALLOC_CTX *ctx,
                if (ptr[0] != attr) break;
        }
 
-       vp = pairalloc(ctx, da);
+       vp = fr_pair_afrom_da(ctx, da);
        if (!vp) return -1;
 
        vp->vp_length = total;
        vp->vp_octets = p = talloc_array(vp, uint8_t, vp->vp_length);
        if (!p) {
-               pairfree(&vp);
+               fr_pair_list_free(&vp);
                return -1;
        }
 
@@ -2879,7 +2931,7 @@ static ssize_t data2vp_concat(TALLOC_CTX *ctx,
 /** Convert TLVs to one or more VPs
  *
  */
-static ssize_t data2vp_tlvs(TALLOC_CTX *ctx,
+ssize_t rad_data2vp_tlvs(TALLOC_CTX *ctx,
                            RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                            char const *secret, DICT_ATTR const *da,
                            uint8_t const *start, size_t length,
@@ -2916,13 +2968,13 @@ static ssize_t data2vp_tlvs(TALLOC_CTX *ctx,
                        my_vendor = da->vendor;
 
                        if (!dict_attr_child(da, &my_attr, &my_vendor)) {
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
                                return -1;
                        }
 
                        child = dict_unknown_afrom_fields(ctx, my_attr, my_vendor);
                        if (!child) {
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
                                return -1;
                        }
                }
@@ -2930,7 +2982,7 @@ static ssize_t data2vp_tlvs(TALLOC_CTX *ctx,
                tlv_len = data2vp(ctx, packet, original, secret, child,
                                  data + 2, data[1] - 2, data[1] - 2, tail);
                if (tlv_len < 0) {
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return -1;
                }
                if (*tail) tail = &((*tail)->next);
@@ -3296,7 +3348,7 @@ create_attrs:
                vsa_len = data2vp_vsa(ctx, packet, original, secret, dv,
                                      data, attrlen, tail);
                if (vsa_len < 0) {
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        fr_strerror_printf("Internal sanity check %d", __LINE__);
                        return -1;
                }
@@ -3337,7 +3389,6 @@ ssize_t data2vp(TALLOC_CTX *ctx,
        ssize_t rcode;
        uint32_t vendor;
        DICT_ATTR const *child;
-       DICT_VENDOR *dv;
        VALUE_PAIR *vp;
        uint8_t const *data = start;
        char *p;
@@ -3450,7 +3501,29 @@ ssize_t data2vp(TALLOC_CTX *ctx,
                                             packet->vector);
                        }
                        buffer[253] = '\0';
-                       datalen = strlen((char *) buffer);
+
+                       /*
+                        *      MS-CHAP-MPPE-Keys are 24 octets, and
+                        *      encrypted.  Since it's binary, we can't
+                        *      look for trailing zeros.
+                        */
+                       if (da->flags.length) {
+                               if (datalen > da->flags.length) {
+                                       datalen = da->flags.length;
+                               } /* else leave datalen alone */
+                       } else {
+                               /*
+                                *      Take off trailing zeros from the END.
+                                *      This allows passwords to have zeros in
+                                *      the middle of a field.
+                                *
+                                *      However, if the password has a zero at
+                                *      the end, it will get mashed by this
+                                *      code.  There's really no way around
+                                *      that.
+                                */
+                               while ((datalen > 0) && (buffer[datalen - 1] == '\0')) datalen--;
+                       }
                        break;
 
                /*
@@ -3630,16 +3703,19 @@ ssize_t data2vp(TALLOC_CTX *ctx,
 
                memcpy(&vendor, data, 4);
                vendor = ntohl(vendor);
-               dv = dict_vendorbyvalue(vendor);
-               if (!dv) {
-                       child = dict_unknown_afrom_fields(ctx, data[4], da->vendor | vendor);
-               } else {
-                       child = dict_attrbyparent(da, data[4], vendor);
-                       if (!child) {
-                               child = dict_unknown_afrom_fields(ctx, data[4], da->vendor | vendor);
-                       }
+               vendor |= da->vendor;
+
+               child = dict_attrbyvalue(data[4], vendor);
+               if (!child) {
+                       /*
+                        *      Create a "raw" attribute from the
+                        *      contents of the EVS VSA.
+                        */
+                       da = dict_unknown_afrom_fields(ctx, data[4], vendor);
+                       data += 5;
+                       datalen -= 5;
+                       break;
                }
-               if (!child) goto raw;
 
                rcode = data2vp(ctx, packet, original, secret, child,
                                data + 5, attrlen - 5, attrlen - 5, pvp);
@@ -3652,8 +3728,8 @@ ssize_t data2vp(TALLOC_CTX *ctx,
                 *      attribute, OR they've already been grouped
                 *      into a contiguous memory buffer.
                 */
-               rcode = data2vp_tlvs(ctx, packet, original, secret, da,
-                                    data, attrlen, pvp);
+               rcode = rad_data2vp_tlvs(ctx, packet, original, secret, da,
+                                        data, attrlen, pvp);
                if (rcode < 0) goto raw;
                return rcode;
 
@@ -3697,7 +3773,7 @@ ssize_t data2vp(TALLOC_CTX *ctx,
         *      information, decode the actual data.
         */
  alloc_cui:
-       vp = pairalloc(ctx, da);
+       vp = fr_pair_afrom_da(ctx, da);
        if (!vp) return -1;
 
        vp->vp_length = datalen;
@@ -3712,7 +3788,7 @@ ssize_t data2vp(TALLOC_CTX *ctx,
                break;
 
        case PW_TYPE_OCTETS:
-               pairmemcpy(vp, data, vp->vp_length);
+               fr_pair_value_memcpy(vp, data, vp->vp_length);
                break;
 
        case PW_TYPE_ABINARY:
@@ -3801,7 +3877,7 @@ ssize_t data2vp(TALLOC_CTX *ctx,
                break;
 
        default:
-               pairfree(&vp);
+               fr_pair_list_free(&vp);
                fr_strerror_printf("Internal sanity check %d", __LINE__);
                return -1;
        }
@@ -3903,7 +3979,6 @@ ssize_t rad_vp2data(uint8_t const **out, VALUE_PAIR const *vp)
        switch (vp->da->type) {
        case PW_TYPE_STRING:
        case PW_TYPE_OCTETS:
-       case PW_TYPE_TLV:
                memcpy(out, &vp->data.ptr, sizeof(*out));
                break;
 
@@ -3972,6 +4047,7 @@ ssize_t rad_vp2data(uint8_t const **out, VALUE_PAIR const *vp)
        case PW_TYPE_LONG_EXTENDED:
        case PW_TYPE_EVS:
        case PW_TYPE_VSA:
+       case PW_TYPE_TLV:
        case PW_TYPE_TIMEVAL:
        case PW_TYPE_MAX:
                fr_strerror_printf("Cannot get data for VALUE_PAIR type %i", vp->da->type);
@@ -4019,7 +4095,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                my_len = rad_attr2vp(packet, packet, original, secret,
                                     ptr, packet_length, &vp);
                if (my_len < 0) {
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return -1;
                }
 
@@ -4040,8 +4116,8 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                    (num_attributes > fr_max_attributes)) {
                        char host_ipaddr[128];
 
-                       pairfree(&head);
-                       fr_strerror_printf("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
+                       fr_pair_list_free(&head);
+                       fr_strerror_printf("Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).",
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
@@ -4233,14 +4309,15 @@ int rad_tunnel_pwencode(char *passwd, size_t *pwlen, char const *secret,
        if (len > 127) len = 127;
 
        /*
-        * Shift the password 3 positions right to place a salt and original
-        * length, tag will be added automatically on packet send
+        *      Shift the password 3 positions right to place a salt and original
+        *      length, tag will be added automatically on packet send.
         */
-       for (n=len ; n>=0 ; n--) passwd[n+3] = passwd[n];
+       for (n = len ; n >= 0 ; n--) passwd[n + 3] = passwd[n];
        salt = passwd;
        passwd += 2;
+
        /*
-        * save original password length as first password character;
+        *      save original password length as first password character;
         */
        *passwd = len;
        len += 1;
@@ -4307,14 +4384,14 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret,
        FR_MD5_CTX  context, old;
        uint8_t         digest[AUTH_VECTOR_LEN];
        int             secretlen;
-       unsigned        i, n, len, reallen;
+       size_t          i, n, encrypted_len, reallen;
 
-       len = *pwlen;
+       encrypted_len = *pwlen;
 
        /*
         *      We need at least a salt.
         */
-       if (len < 2) {
+       if (encrypted_len < 2) {
                fr_strerror_printf("tunnel password is too short");
                return -1;
        }
@@ -4329,13 +4406,13 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret,
         *      more data.  So the 'data_len' field may be wrong,
         *      but that's ok...
         */
-       if (len <= 3) {
+       if (encrypted_len <= 3) {
                passwd[0] = 0;
                *pwlen = 0;
                return 0;
        }
 
-       len -= 2;               /* discount the salt */
+       encrypted_len -= 2;             /* discount the salt */
 
        /*
         *      Use the secret to setup the decryption digest
@@ -4355,10 +4432,20 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret,
        fr_md5_update(&context, passwd, 2);
 
        reallen = 0;
-       for (n = 0; n < len; n += AUTH_PASS_LEN) {
-               int base = 0;
+       for (n = 0; n < encrypted_len; n += AUTH_PASS_LEN) {
+               size_t base;
+               size_t block_len = AUTH_PASS_LEN;
+
+               /*
+                *      Ensure we don't overflow the input on MD5
+                */
+               if ((n + 2 + AUTH_PASS_LEN) > *pwlen) {
+                       block_len = *pwlen - n - 2;
+               }
 
                if (n == 0) {
+                       base = 1;
+
                        fr_md5_final(digest, &context);
 
                        context = old;
@@ -4369,31 +4456,27 @@ int rad_tunnel_pwdecode(uint8_t *passwd, size_t *pwlen, char const *secret,
                         *      'data_len' field.  Ensure it's sane.
                         */
                        reallen = passwd[2] ^ digest[0];
-                       if (reallen >len) {
+                       if (reallen > encrypted_len) {
                                fr_strerror_printf("tunnel password is too long for the attribute");
                                return -1;
                        }
 
-                       fr_md5_update(&context, passwd + 2, AUTH_PASS_LEN);
+                       fr_md5_update(&context, passwd + 2, block_len);
 
-                       base = 1;
                } else {
+                       base = 0;
+
                        fr_md5_final(digest, &context);
 
                        context = old;
-                       fr_md5_update(&context, passwd + n + 2, AUTH_PASS_LEN);
+                       fr_md5_update(&context, passwd + n + 2, block_len);
                }
 
-               for (i = base; i < AUTH_PASS_LEN; i++) {
+               for (i = base; i < block_len; i++) {
                        passwd[n + i - 1] = passwd[n + i + 2] ^ digest[i];
                }
        }
 
-       /*
-        *      See make_tunnel_password, above.
-        */
-       if (reallen > 239) reallen = 239;
-
        *pwlen = reallen;
        passwd[reallen] = 0;
 
@@ -4441,7 +4524,7 @@ int rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output, int id,
         *      Use Chap-Challenge pair if present,
         *      Request Authenticator otherwise.
         */
-       challenge = pairfind(packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
+       challenge = fr_pair_find_by_num(packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
        if (challenge) {
                memcpy(ptr, challenge->vp_strvalue, challenge->vp_length);
                i += challenge->vp_length;
@@ -4623,7 +4706,7 @@ void rad_free(RADIUS_PACKET **radius_packet_ptr)
 
        VERIFY_PACKET(radius_packet);
 
-       pairfree(&radius_packet->vps);
+       fr_pair_list_free(&radius_packet->vps);
 
        talloc_free(radius_packet);
        *radius_packet_ptr = NULL;
@@ -4656,7 +4739,7 @@ RADIUS_PACKET *rad_copy_packet(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
        out->data = NULL;
        out->data_len = 0;
 
-       out->vps = paircopy(out, in->vps);
+       out->vps = fr_pair_list_copy(out, in->vps);
        out->offset = 0;
 
        return out;
index ce2b4f9..0428c58 100644 (file)
@@ -361,7 +361,7 @@ int regex_exec(regex_t *preg, char const *subject, size_t len, regmatch_t pmatch
                        regerror(ret, preg, errbuf, sizeof(errbuf));
 
                        fr_strerror_printf("regex evaluation failed: %s", errbuf);
-                       *nmatch = 0;
+                       if (nmatch) *nmatch = 0;
                        return -1;
                }
                return 0;
index f99248e..59545bb 100644 (file)
@@ -91,7 +91,7 @@ void fr_sha1_transform(uint32_t state[5], uint8_t const buffer[64])
 
 /* fr_sha1_init - Initialize new context */
 
-void fr_sha1_init(fr_SHA1_CTX* context)
+void fr_sha1_init(fr_sha1_ctx* context)
 {
        /* SHA1 initialization constants */
        context->state[0] = 0x67452301;
@@ -103,7 +103,7 @@ void fr_sha1_init(fr_SHA1_CTX* context)
 }
 
 /* Run your data through this. */
-void fr_sha1_update(fr_SHA1_CTX *context,uint8_t const *data, size_t len)
+void fr_sha1_update(fr_sha1_ctx *context,uint8_t const *data, size_t len)
 {
        unsigned int i, j;
 
@@ -129,7 +129,7 @@ void fr_sha1_update(fr_SHA1_CTX *context,uint8_t const *data, size_t len)
 
 /* Add padding and return the message digest. */
 
-void fr_sha1_final(uint8_t digest[20], fr_SHA1_CTX *context)
+void fr_sha1_final(uint8_t digest[20], fr_sha1_ctx *context)
 {
        uint32_t i, j;
        uint8_t finalcount[8];
@@ -162,7 +162,7 @@ void fr_sha1_final(uint8_t digest[20], fr_SHA1_CTX *context)
 #  endif
 }
 
-void fr_sha1_final_no_len(uint8_t digest[20], fr_SHA1_CTX *context)
+void fr_sha1_final_no_len(uint8_t digest[20], fr_sha1_ctx *context)
 {
        uint32_t i, j;
 
diff --git a/src/lib/socket.c b/src/lib/socket.c
new file mode 100644 (file)
index 0000000..3c88b78
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ *   This program is 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$
+ * @file socket.c
+ * @brief Functions for establishing and managing low level sockets.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @author Alan DeKok <aland@freeradius.org>
+ *
+ * @copyright 2015 The FreeRADIUS project
+ */
+ #include <freeradius-devel/libradius.h>
+
+#ifdef HAVE_SYS_UN_H
+#  include <sys/un.h>
+#  ifndef SUN_LEN
+#    define SUN_LEN(su)  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
+#  endif
+
+/** Open a Unix socket
+ *
+ * @note If the file doesn't exist then errno will be set to ENOENT.
+ *
+ * The following code demonstrates using this function with a connection timeout:
+ @code {.c}
+   sockfd = fr_socket_client_unix(path, true);
+   if (sockfd < 0) {
+       fr_perror();
+       exit(1);
+}
+   if ((errno == EINPROGRESS) && (fr_socket_wait_for_connect(sockfd, timeout) < 0)) {
+   error:
+       fr_perror();
+       close(sockfd);
+       goto error;
+}
+//Optionally, if blocking operation is required
+   if (fr_blocking(sockfd) < 0) goto error;
+ @endcode
+ *
+ * @param path to the file bound to the unix socket.
+ * @param async Whether to set the socket to nonblocking, allowing use of
+ *     #fr_socket_wait_for_connect.
+ * @return socket FD on success, -1 on error.
+ */
+int fr_socket_client_unix(char const *path, bool async)
+{
+       int                     sockfd = -1;
+       size_t                  len;
+       socklen_t               socklen;
+       struct sockaddr_un      saremote;
+
+       len = strlen(path);
+       if (len >= sizeof(saremote.sun_path)) {
+               fr_strerror_printf("Path too long, maximum length is %zu", sizeof(saremote.sun_path) - 1);
+               errno = EINVAL;
+               return -1;
+       }
+
+       sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (sockfd < 0) {
+               fr_strerror_printf("Failed creating UNIX socket: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       if (async && (fr_nonblock(sockfd) < 0)) {
+               close(sockfd);
+               return -1;
+       }
+
+       saremote.sun_family = AF_UNIX;
+       memcpy(saremote.sun_path, path, len + 1); /* SUN_LEN does strlen */
+
+       socklen = SUN_LEN(&saremote);
+
+       /*
+        *      Although we ignore SIGPIPE, some operating systems
+        *      like BSD and OSX ignore the ignoring.
+        *
+        *      Fortunately, those operating systems usually support
+        *      SO_NOSIGPIPE, to prevent them raising the signal in
+        *      the first place.
+        */
+#ifdef SO_NOSIGPIPE
+       {
+               int set = 1;
+
+               setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
+       }
+#endif
+
+       if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
+               /*
+                *      POSIX says the only time we will get this,
+                *      is if the socket has been marked as
+                *      nonblocking. This is not an error, the caller
+                *      must check the state of errno, and wait for
+                *      the connection to complete.
+                */
+               if (errno == EINPROGRESS) return sockfd;
+
+               close(sockfd);
+               fr_strerror_printf("Failed connecting to %s: %s", path, fr_syserror(errno));
+
+               return -1;
+       }
+       return sockfd;
+}
+#else
+int fr_socket_client_unix(UNUSED char const *path, UNUSED bool async)
+{
+       fprintf(stderr, "Unix domain sockets not supported on this system");
+       return -1;
+}
+#endif /* WITH_SYS_UN_H */
+
+/** Establish a connected TCP socket
+ *
+ * The following code demonstrates using this function with a connection timeout:
+ @code {.c}
+   sockfd = fr_socket_client_tcp(NULL, ipaddr, port, true);
+   if (sockfd < 0) {
+       fr_perror();
+       exit(1);
+}
+   if ((errno == EINPROGRESS) && (fr_socket_wait_for_connect(sockfd, timeout) < 0)) {
+   error:
+       fr_perror();
+       close(sockfd);
+       goto error;
+}
+//Optionally, if blocking operation is required
+   if (fr_blocking(sockfd) < 0) goto error;
+ @endcode
+ *
+ * @param src_ipaddr to bind socket to, may be NULL if socket is not bound to any specific
+ *     address.
+ * @param dst_ipaddr Where to connect to.
+ * @param dst_port Where to connect to.
+ * @param async Whether to set the socket to nonblocking, allowing use of
+ *     #fr_socket_wait_for_connect.
+ * @return FD on success, -1 on failure.
+ */
+int fr_socket_client_tcp(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, bool async)
+{
+       int sockfd;
+       struct sockaddr_storage salocal;
+       socklen_t       salen;
+
+       if (!dst_ipaddr) return -1;
+
+       sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
+       if (sockfd < 0) {
+               fr_strerror_printf("Error creating TCP socket: %s", fr_syserror(errno));
+               return sockfd;
+       }
+
+       if (async && (fr_nonblock(sockfd) < 0)) {
+               close(sockfd);
+               return -1;
+       }
+
+       /*
+        *      Allow the caller to bind us to a specific source IP.
+        */
+       if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
+               if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
+                       close(sockfd);
+                       return -1;
+               }
+
+               if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
+                       fr_strerror_printf("Failure binding to IP: %s", fr_syserror(errno));
+                       close(sockfd);
+                       return -1;
+               }
+       }
+
+       if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
+               close(sockfd);
+               return -1;
+       }
+
+       /*
+        *      Although we ignore SIGPIPE, some operating systems
+        *      like BSD and OSX ignore the ignoring.
+        *
+        *      Fortunately, those operating systems usually support
+        *      SO_NOSIGPIPE, to prevent them raising the signal in
+        *      the first place.
+        */
+#ifdef SO_NOSIGPIPE
+       {
+               int set = 1;
+
+               setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
+       }
+#endif
+
+       if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
+               /*
+                *      POSIX says the only time we will get this,
+                *      is if the socket has been marked as
+                *      nonblocking. This is not an error, the caller
+                *      must check the state of errno, and wait for
+                *      the connection to complete.
+                */
+               if (errno == EINPROGRESS) return sockfd;
+
+               fr_strerror_printf("Failed connecting socket: %s", fr_syserror(errno));
+               close(sockfd);
+               return -1;
+       }
+
+       return sockfd;
+}
+
+/** Establish a connected UDP socket
+ *
+ * Connected UDP sockets can be used with write(), unlike unconnected sockets
+ * which must be used with sendto and recvfrom.
+ *
+ * The following code demonstrates using this function with a connection timeout:
+ @code {.c}
+   sockfd = fr_socket_client_udp(NULL, ipaddr, port, true);
+   if (sockfd < 0) {
+       fr_perror();
+       exit(1);
+}
+   if ((errno == EINPROGRESS) && (fr_socket_wait_for_connect(sockfd, timeout) < 0)) {
+   error:
+       fr_perror();
+       close(sockfd);
+       goto error;
+}
+//Optionally, if blocking operation is required
+   if (fr_blocking(sockfd) < 0) goto error;
+ @endcode
+ *
+ * @param src_ipaddr to bind socket to, may be NULL if socket is not bound to any specific
+ *     address.
+ * @param dst_ipaddr Where to send datagrams.
+ * @param dst_port Where to send datagrams.
+ * @param async Whether to set the socket to nonblocking, allowing use of
+ *     #fr_socket_wait_for_connect.
+ * @return FD on success, -1 on failure.
+ */
+int fr_socket_client_udp(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, bool async)
+{
+       int                     sockfd;
+       struct sockaddr_storage salocal;
+       socklen_t               salen;
+
+       if (!dst_ipaddr) return -1;
+
+       sockfd = socket(dst_ipaddr->af, SOCK_DGRAM, 0);
+       if (sockfd < 0) {
+               fr_strerror_printf("Error creating UDP socket: %s", fr_syserror(errno));
+               return sockfd;
+       }
+
+       if (async && (fr_nonblock(sockfd) < 0)) {
+               close(sockfd);
+               return -1;
+       }
+
+       /*
+        *      Allow the caller to bind us to a specific source IP.
+        */
+       if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
+               if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
+                       close(sockfd);
+                       return -1;
+               }
+
+               if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
+                       fr_strerror_printf("Failure binding to IP: %s", fr_syserror(errno));
+                       close(sockfd);
+                       return -1;
+               }
+       }
+
+       if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
+               close(sockfd);
+               return -1;
+       }
+
+       /*
+        *      Although we ignore SIGPIPE, some operating systems
+        *      like BSD and OSX ignore the ignoring.
+        *
+        *      Fortunately, those operating systems usually support
+        *      SO_NOSIGPIPE, to prevent them raising the signal in
+        *      the first place.
+        */
+#ifdef SO_NOSIGPIPE
+       {
+               int set = 1;
+
+               setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
+       }
+#endif
+
+       if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
+               /*
+                *      POSIX says the only time we will get this,
+                *      is if the socket has been marked as
+                *      nonblocking. This is not an error, the caller
+                *      must check the state of errno, and wait for
+                *      the connection to complete.
+                */
+               if (errno == EINPROGRESS) return sockfd;
+
+               fr_strerror_printf("Failed connecting socket: %s", fr_syserror(errno));
+               close(sockfd);
+               return -1;
+       }
+
+       return sockfd;
+}
+
+/** Wait for a socket to be connected, with an optional timeout
+ *
+ * @note On error the caller is expected to ``close(sockfd)``.
+ *
+ * @param sockfd the socket to wait on.
+ * @param timeout How long to wait for socket to open.
+ * @return 0 on success, -1 on connection error, -2 on timeout, -3 on select error.
+ */
+int fr_socket_wait_for_connect(int sockfd, struct timeval *timeout)
+{
+       int     ret;
+       fd_set  error_set;
+       fd_set  write_set;      /* POSIX says sockets are open when they become writeable */
+
+       FD_ZERO(&error_set);
+       FD_ZERO(&write_set);
+
+       FD_SET(sockfd, &error_set);
+       FD_SET(sockfd, &write_set);
+
+       /* Don't let signals mess up the select */
+       do {
+               ret = select(sockfd + 1, NULL, &write_set, &error_set, timeout);
+       } while ((ret == -1) && (errno == EINTR));
+
+       switch (ret) {
+       case 1: /* ok (maybe) */
+       {
+               int error;
+               socklen_t socklen = sizeof(error);
+
+               if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&error, &socklen)) {
+                       fr_strerror_printf("Failed connecting socket: %s", fr_syserror(errno));
+                       return -1;
+               }
+
+               if (FD_ISSET(sockfd, &error_set)) {
+                       fr_strerror_printf("Failed connecting socket: Unknown error");
+                       return -1;
+               }
+       }
+               return 0;
+
+       case 0: /* timeout */
+               if (!fr_assert(timeout)) return -1;
+               fr_strerror_printf("Connection timed out after %" PRIu64"ms",
+                                  (timeout->tv_sec * (uint64_t)1000) + (timeout->tv_usec / 1000));
+               return -2;
+
+       case -1: /* select error */
+               fr_strerror_printf("Failed waiting for connection: %s", fr_syserror(errno));
+               return -3;
+
+       default:
+               fr_assert(0);
+               return -1;
+       }
+}
index f8016c9..c4ebe21 100644 (file)
@@ -29,60 +29,6 @@ RCSID("$Id$")
 /* FIXME: into common RADIUS header? */
 #define MAX_PACKET_LEN 4096
 
-/*
- *     Open a socket TO the given IP and port.
- */
-int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
-                        fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
-{
-       int sockfd;
-       struct sockaddr_storage salocal;
-       socklen_t       salen;
-
-       if (!dst_ipaddr) return -1;
-
-       sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
-       if (sockfd < 0) {
-               return sockfd;
-       }
-
-       /*
-        *      Allow the caller to bind us to a specific source IP.
-        */
-       if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
-               if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
-                       close(sockfd);
-                       return -1;
-               }
-
-               if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
-                       fr_strerror_printf("Failure binding to IP: %s",
-                                          fr_syserror(errno));
-                       close(sockfd);
-                       return -1;
-               }
-       }
-
-       if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
-               close(sockfd);
-               return -1;
-       }
-
-       /*
-        *      FIXME: If EINPROGRESS, then tell the caller that
-        *      somehow.  The caller can then call connect() when the
-        *      socket is ready...
-        */
-       if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
-               fr_strerror_printf("Failed in connect(): %s", fr_syserror(errno));
-               close(sockfd);
-               return -1;
-       }
-
-       return sockfd;
-}
-
-
 RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags)
 {
        RADIUS_PACKET *packet = rad_alloc(NULL, false);
@@ -207,7 +153,7 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
         */
        packet->vps = NULL;
 
-       if (fr_debug_flag) {
+       if (fr_debug_lvl) {
                char ip_buf[128], buffer[256];
 
                if (packet->src_ipaddr.af != AF_UNSPEC) {
index 0e8cef7..3907908 100644 (file)
@@ -6,8 +6,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -70,14 +69,14 @@ const bool fr_assignment_op[] = {
        true,           /* := */
        true,           /* = */
        false,          /* != */
-       true,           /* >= */
-       true,           /* >            15 */
-       true,           /* <= */
-       true,           /* < */
+       false,          /* >= */
+       false,          /* >            15 */
+       false,          /* <= */
+       false,          /* < */
        false,          /* =~ */
        false,          /* !~ */
        false,          /* =*           20 */
-       true,           /* !* */
+       false,          /* !* */
        false,          /* == */
        false,                          /* # */
        false,          /* bare word */
index 75c5c0e..ce0147d 100644 (file)
@@ -1,8 +1,7 @@
 /*
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
index f9026ed..5f2ad75 100644 (file)
@@ -1,12 +1,11 @@
 /*
- * valuepair.c Functions to handle value_data_t
+ * value.c     Functions to handle value_data_t
  *
  * Version:    $Id$
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -443,19 +442,18 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
        switch (*src_type) {
        case PW_TYPE_STRING:
        {
-               char            *p;
+               char            *p, *buff;
                char const      *q;
                int             x;
 
-               dst->strvalue = p = talloc_memdup(ctx, src, len + 1);
-               p[len] = '\0';
-               talloc_set_type(p, char);
+               buff = p = talloc_bstrndup(ctx, src, len);
 
                /*
                 *      No de-quoting.  Just copy the string.
                 */
                if (!quote) {
                        ret = len;
+                       dst->strvalue = buff;
                        goto finish;
                }
 
@@ -467,14 +465,23 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                if (quote == '\'') {
                        q = p;
 
-                       /*
-                        *      Escape ONLY the quotation character.
-                        *      Everything else is left as-is.
-                        */
-                       while (q < (dst->strvalue + len)) {
+                       while (q < (buff + len)) {
+                               /*
+                                *      The quotation character is escaped.
+                                */
                                if ((q[0] == '\\') &&
-                                   (q[1] == '\'')) {
-                                       *(p++) = '\'';
+                                   (q[1] == quote)) {
+                                       *(p++) = quote;
+                                       q += 2;
+                                       continue;
+                               }
+
+                               /*
+                                *      Two backslashes get mangled to one.
+                                */
+                               if ((q[0] == '\\') &&
+                                   (q[1] == '\\')) {
+                                       *(p++) = '\\';
                                        q += 2;
                                        continue;
                                }
@@ -486,8 +493,10 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                        }
 
                        *p = '\0';
-                       ret = p - dst->strvalue;
-                       dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
+                       ret = p - buff;
+
+                       /* Shrink the buffer to the correct size */
+                       dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
                        goto finish;
                }
 
@@ -496,11 +505,12 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                 *      escaping.
                 */
                q = p;
-               while (q < (dst->strvalue + len)) {
+               while (q < (buff + len)) {
                        char c = *q++;
 
-                       if ((c == '\\') && (q >= (dst->strvalue + len))) {
+                       if ((c == '\\') && (q >= (buff + len))) {
                                fr_strerror_printf("Invalid escape at end of string");
+                               talloc_free(buff);
                                return -1;
                        }
 
@@ -566,8 +576,8 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                }
 
                *p = '\0';
-               ret = p - dst->strvalue;
-               dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
+               ret = p - buff;
+               dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
        }
                goto finish;
 
@@ -590,14 +600,13 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                        goto finish;
                }
 
-       do_octets:
                len -= 2;
 
                /*
                 *      Invalid.
                 */
                if ((len & 0x01) != 0) {
-                       fr_strerror_printf("Length of Hex String is not even, got %zu bytes", ret);
+                       fr_strerror_printf("Length of Hex String is not even, got %zu bytes", len);
                        return -1;
                }
 
@@ -615,12 +624,25 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
 
        case PW_TYPE_ABINARY:
 #ifdef WITH_ASCEND_BINARY
-               if ((len > 1) && (strncasecmp(src, "0x", 2) == 0)) goto do_octets;
+               if ((len > 1) && (strncasecmp(src, "0x", 2) == 0)) {
+                       ssize_t bin;
 
-               if (ascend_parse_filter(dst, src, len) < 0 ) {
-                       /* Allow ascend_parse_filter's strerror to bubble up */
-                       return -1;
+                       if (len > ((sizeof(dst->filter) + 1) * 2)) {
+                               fr_strerror_printf("Hex data is too large for ascend filter");
+                               return -1;
+                       }
+
+                       bin = fr_hex2bin((uint8_t *) &dst->filter, ret, src + 2, len);
+                       if (bin < ret) {
+                               memset(((uint8_t *) &dst->filter) + bin, 0, ret - bin);
+                       }
+               } else {
+                       if (ascend_parse_filter(dst, src, len) < 0 ) {
+                               /* Allow ascend_parse_filter's strerror to bubble up */
+                               return -1;
+                       }
                }
+
                ret = sizeof(dst->filter);
                goto finish;
 #else
@@ -634,29 +656,8 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
 
        /* don't use this! */
        case PW_TYPE_TLV:
-       {
-               uint8_t *p;
-
-               if ((len < 2) || (len & 0x01) || (strncasecmp(src, "0x", 2) != 0)) {
-                       fr_strerror_printf("Invalid TLV specification");
-                       return -1;
-               }
-               len -= 2;
-
-               ret = len >> 1;
-               p = talloc_array(ctx, uint8_t, ret);
-               if (!p) {
-                       fr_strerror_printf("No memory");
-                       return -1;
-               }
-               if (fr_hex2bin(p, ret, src + 2, len) != (size_t)ret) {
-                       fr_strerror_printf("Invalid hex data in TLV");
-                       return -1;
-               }
-
-               dst->tlv = p;
-       }
-               goto finish;
+               fr_strerror_printf("Cannot parse TLV");
+               return -1;
 
        case PW_TYPE_IPV4_ADDR:
        {
@@ -1040,7 +1041,7 @@ ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
         *      Serialise a value_data_t
         */
        if (dst_type == PW_TYPE_STRING) {
-               dst->strvalue = vp_data_aprints_value(ctx, src_type, src_enumv, src, src_len, '\0');
+               dst->strvalue = value_data_aprints(ctx, src_type, src_enumv, src, src_len, '\0');
                return talloc_array_length(dst->strvalue) - 1;
        }
 
@@ -1087,6 +1088,10 @@ ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
                        dst->integer64 = src->integer;
                        break;
 
+               case PW_TYPE_DATE:
+                       dst->integer64 = src->date;
+                       break;
+
                case PW_TYPE_OCTETS:
                        goto do_octets;
 
@@ -1140,6 +1145,45 @@ ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
        }
 
        /*
+        *      We can cast integers less that < INT_MAX to signed
+        */
+       if (dst_type == PW_TYPE_SIGNED) {
+               switch (src_type) {
+               case PW_TYPE_BYTE:
+                       dst->sinteger = src->byte;
+                       break;
+
+               case PW_TYPE_SHORT:
+                       dst->sinteger = src->ushort;
+                       break;
+
+               case PW_TYPE_INTEGER:
+                       if (src->integer > INT_MAX) {
+                               fr_strerror_printf("Invalid cast: From integer to signed.  integer value %u is larger "
+                                                  "than max signed int and would overflow", src->integer);
+                               return -1;
+                       }
+                       dst->sinteger = (int)src->integer;
+                       break;
+
+               case PW_TYPE_INTEGER64:
+                       if (src->integer > INT_MAX) {
+                               fr_strerror_printf("Invalid cast: From integer64 to signed.  integer64 value %" PRIu64
+                                                  " is larger than max signed int and would overflow", src->integer64);
+                               return -1;
+                       }
+                       dst->sinteger = (int)src->integer64;
+                       break;
+
+               case PW_TYPE_OCTETS:
+                       goto do_octets;
+
+               default:
+                       goto invalid_cast;
+               }
+               goto fixed_length;
+       }
+       /*
         *      Conversions between IPv4 addresses, IPv6 addresses, IPv4 prefixes and IPv6 prefixes
         *
         *      For prefix to ipaddress conversions, we assume that the host portion has already
@@ -1377,9 +1421,8 @@ ssize_t value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE src_type,
                break;
 
        case PW_TYPE_STRING:
-               dst->strvalue = talloc_memdup(ctx, src->strvalue, src_len + 1);
+               dst->strvalue = talloc_bstrndup(ctx, src->strvalue, src_len);
                if (!dst->strvalue) return -1;
-               talloc_set_type(dst->strvalue, char);
                break;
 
        case PW_TYPE_OCTETS:
@@ -1391,3 +1434,411 @@ ssize_t value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE src_type,
 
        return src_len;
 }
+
+
+
+/** Print one attribute value to a string
+ *
+ */
+char *value_data_aprints(TALLOC_CTX *ctx,
+                        PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
+                        size_t inlen, char quote)
+{
+       char *p = NULL;
+       unsigned int i;
+
+       switch (type) {
+       case PW_TYPE_STRING:
+       {
+               size_t len, ret;
+
+               if (!quote) {
+                       p = talloc_bstrndup(ctx, data->strvalue, inlen);
+                       if (!p) return NULL;
+                       talloc_set_type(p, char);
+                       return p;
+               }
+
+               /* Gets us the size of the buffer we need to alloc */
+               len = fr_prints_len(data->strvalue, inlen, quote);
+               p = talloc_array(ctx, char, len);
+               if (!p) return NULL;
+
+               ret = fr_prints(p, len, data->strvalue, inlen, quote);
+               if (!fr_assert(ret == (len - 1))) {
+                       talloc_free(p);
+                       return NULL;
+               }
+               break;
+       }
+
+       case PW_TYPE_INTEGER:
+               i = data->integer;
+               goto print_int;
+
+       case PW_TYPE_SHORT:
+               i = data->ushort;
+               goto print_int;
+
+       case PW_TYPE_BYTE:
+               i = data->byte;
+
+       print_int:
+       {
+               DICT_VALUE const *dv;
+
+               if (enumv && (dv = dict_valbyattr(enumv->attr, enumv->vendor, i))) {
+                       p = talloc_typed_strdup(ctx, dv->name);
+               } else {
+                       p = talloc_typed_asprintf(ctx, "%u", i);
+               }
+       }
+               break;
+
+       case PW_TYPE_SIGNED:
+               p = talloc_typed_asprintf(ctx, "%d", data->sinteger);
+               break;
+
+       case PW_TYPE_INTEGER64:
+               p = talloc_typed_asprintf(ctx, "%" PRIu64 , data->integer64);
+               break;
+
+       case PW_TYPE_ETHERNET:
+               p = talloc_typed_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
+                                         data->ether[0], data->ether[1],
+                                         data->ether[2], data->ether[3],
+                                         data->ether[4], data->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, (uint8_t *) &data->filter, inlen, 0);
+               break;
+#else
+                 /* FALL THROUGH */
+#endif
+
+       case PW_TYPE_OCTETS:
+               p = talloc_array(ctx, char, 2 + 1 + inlen * 2);
+               if (!p) return NULL;
+               p[0] = '0';
+               p[1] = 'x';
+
+               fr_bin2hex(p + 2, data->octets, inlen);
+               break;
+
+       case PW_TYPE_DATE:
+       {
+               time_t t;
+               struct tm s_tm;
+
+               t = data->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';
+               value_data_prints(buff, sizeof(buff), type, enumv, data, inlen, '\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';
+               value_data_prints(buff, sizeof(buff), type, enumv, data, inlen, '\0');
+
+               p = talloc_typed_strdup(ctx, buff);
+       }
+       break;
+
+       case PW_TYPE_IFID:
+               p = talloc_typed_asprintf(ctx, "%x:%x:%x:%x",
+                                         (data->ifid[0] << 8) | data->ifid[1],
+                                         (data->ifid[2] << 8) | data->ifid[3],
+                                         (data->ifid[4] << 8) | data->ifid[5],
+                                         (data->ifid[6] << 8) | data->ifid[7]);
+               break;
+
+       case PW_TYPE_BOOLEAN:
+               p = talloc_typed_strdup(ctx, data->byte ? "yes" : "no");
+               break;
+
+       /*
+        *      Don't add default here
+        */
+       case PW_TYPE_INVALID:
+       case PW_TYPE_COMBO_IP_ADDR:
+       case PW_TYPE_COMBO_IP_PREFIX:
+       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);
+               return NULL;
+       }
+
+       return p;
+}
+
+
+/** Print the value of an attribute to a string
+ *
+ * @note return value should be checked with is_truncated.
+ * @note Will always \0 terminate unless outlen == 0.
+ *
+ * @param out Where to write the printed version of the attribute value.
+ * @param outlen Length of the output buffer.
+ * @param type of data being printed.
+ * @param enumv Enumerated string values for integer types.
+ * @param data to print.
+ * @param inlen Length of data.
+ * @param quote char to escape in string output.
+ * @return  the number of bytes written to the out buffer, or a number >= outlen if truncation has occurred.
+ */
+size_t value_data_prints(char *out, size_t outlen,
+                        PW_TYPE type, DICT_ATTR const *enumv, value_data_t const *data,
+                        ssize_t inlen, char quote)
+{
+       DICT_VALUE      *v;
+       char            buf[1024];      /* Interim buffer to use with poorly behaved printing functions */
+       char const      *a = NULL;
+       char            *p = out;
+       time_t          t;
+       struct tm       s_tm;
+       unsigned int    i;
+
+       size_t          len = 0, freespace = outlen;
+
+       if (!data) return 0;
+       if (outlen == 0) return inlen;
+
+       *out = '\0';
+
+       p = out;
+
+       switch (type) {
+       case PW_TYPE_STRING:
+
+               /*
+                *      Ensure that WE add the quotation marks around the string.
+                */
+               if (quote) {
+                       if (freespace < 3) return inlen + 2;
+
+                       *p++ = quote;
+                       freespace--;
+
+                       len = fr_prints(p, freespace, data->strvalue, inlen, quote);
+                       /* always terminate the quoted string with another quote */
+                       if (len >= (freespace - 1)) {
+                               /* Use out not p as we're operating on the entire buffer */
+                               out[outlen - 2] = (char) quote;
+                               out[outlen - 1] = '\0';
+                               return len + 2;
+                       }
+                       p += len;
+                       freespace -= len;
+
+                       *p++ = (char) quote;
+                       freespace--;
+                       *p = '\0';
+
+                       return len + 2;
+               }
+
+               return fr_prints(out, outlen, data->strvalue, inlen, quote);
+
+       case PW_TYPE_INTEGER:
+               i = data->integer;
+               goto print_int;
+
+       case PW_TYPE_SHORT:
+               i = data->ushort;
+               goto print_int;
+
+       case PW_TYPE_BYTE:
+               i = data->byte;
+
+print_int:
+               /* Normal, non-tagged attribute */
+               if (enumv && (v = dict_valbyattr(enumv->attr, enumv->vendor, i)) != NULL) {
+                       a = v->name;
+                       len = strlen(a);
+               } else {
+                       /* should never be truncated */
+                       len = snprintf(buf, sizeof(buf), "%u", i);
+                       a = buf;
+               }
+               break;
+
+       case PW_TYPE_INTEGER64:
+               return snprintf(out, outlen, "%" PRIu64, data->integer64);
+
+       case PW_TYPE_DATE:
+               t = data->date;
+               if (quote > 0) {
+                       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));
+               }
+               a = buf;
+               break;
+
+       case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
+               len = snprintf(buf, sizeof(buf), "%d", data->sinteger);
+               a = buf;
+               break;
+
+       case PW_TYPE_IPV4_ADDR:
+               a = inet_ntop(AF_INET, &(data->ipaddr), buf, sizeof(buf));
+               len = strlen(buf);
+               break;
+
+       case PW_TYPE_ABINARY:
+#ifdef WITH_ASCEND_BINARY
+               print_abinary(buf, sizeof(buf), (uint8_t const *) data->filter, inlen, quote);
+               a = buf;
+               len = strlen(buf);
+               break;
+#else
+       /* FALL THROUGH */
+#endif
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_TLV:
+       {
+               size_t max;
+
+               /* Return the number of bytes we would have written */
+               len = (inlen * 2) + 2;
+               if (freespace <= 1) {
+                       return len;
+               }
+
+               *out++ = '0';
+               freespace--;
+
+               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, data->octets, ((size_t)inlen > max) ? max : (size_t)inlen);
+       }
+               return len;
+
+       case PW_TYPE_IFID:
+               a = ifid_ntoa(buf, sizeof(buf), data->ifid);
+               len = strlen(buf);
+               break;
+
+       case PW_TYPE_IPV6_ADDR:
+               a = inet_ntop(AF_INET6, &data->ipv6addr, buf, sizeof(buf));
+               len = strlen(buf);
+               break;
+
+       case PW_TYPE_IPV6_PREFIX:
+       {
+               struct in6_addr addr;
+
+               /*
+                *      Alignment issues.
+                */
+               memcpy(&addr, &(data->ipv6prefix[2]), sizeof(addr));
+
+               a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
+               if (a) {
+                       p = buf;
+
+                       len = strlen(buf);
+                       p += len;
+                       len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) data->ipv6prefix[1]);
+               }
+       }
+               break;
+
+       case PW_TYPE_IPV4_PREFIX:
+       {
+               struct in_addr addr;
+
+               /*
+                *      Alignment issues.
+                */
+               memcpy(&addr, &(data->ipv4prefix[2]), sizeof(addr));
+
+               a = inet_ntop(AF_INET, &addr, buf, sizeof(buf));
+               if (a) {
+                       p = buf;
+
+                       len = strlen(buf);
+                       p += len;
+                       len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) (data->ipv4prefix[1] & 0x3f));
+               }
+       }
+               break;
+
+       case PW_TYPE_ETHERNET:
+               return snprintf(out, outlen, "%02x:%02x:%02x:%02x:%02x:%02x",
+                               data->ether[0], data->ether[1],
+                               data->ether[2], data->ether[3],
+                               data->ether[4], data->ether[5]);
+
+       /*
+        *      Don't add default here
+        */
+       case PW_TYPE_INVALID:
+       case PW_TYPE_COMBO_IP_ADDR:
+       case PW_TYPE_COMBO_IP_PREFIX:
+       case PW_TYPE_EXTENDED:
+       case PW_TYPE_LONG_EXTENDED:
+       case PW_TYPE_EVS:
+       case PW_TYPE_VSA:
+       case PW_TYPE_TIMEVAL:
+       case PW_TYPE_BOOLEAN:
+       case PW_TYPE_MAX:
+               fr_assert(0);
+               *out = '\0';
+               return 0;
+       }
+
+       if (a) strlcpy(out, a, outlen);
+
+       return len;     /* Return the number of bytes we would of written (for truncation detection) */
+}
+
index c82dc66..90a0dd8 100644 (file)
@@ -84,7 +84,7 @@ int rad_accounting(REQUEST *request)
                 *      Do the data storage before proxying. This is to ensure
                 *      that we log the packet, even if the proxy never does.
                 */
-               vp = pairfind(request->config_items, PW_ACCT_TYPE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->config, PW_ACCT_TYPE, 0, TAG_ANY);
                if (vp) {
                        acct_type = vp->vp_integer;
                        DEBUG2("  Found Acct-Type %s",
@@ -122,7 +122,7 @@ int rad_accounting(REQUEST *request)
                 *      Maybe one of the preacct modules has decided
                 *      that a proxy should be used.
                 */
-               if ((vp = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY))) {
+               if ((vp = fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY))) {
                        REALM *realm;
 
                        /*
@@ -132,7 +132,7 @@ int rad_accounting(REQUEST *request)
                        realm = realm_find2(vp->vp_strvalue);
                        if (realm && !realm->acct_pool) {
                                DEBUG("rad_accounting: Cancelling proxy to realm %s, as it is a LOCAL realm.", realm->name);
-                               pairdelete(&request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY);
+                               fr_pair_delete_by_num(&request->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
                        } else {
                                /*
                                 *      Don't reply to the NAS now because
@@ -144,6 +144,13 @@ int rad_accounting(REQUEST *request)
                }
        }
 
+#ifdef WITH_PROXY
+       /*
+        *      We didn't see a reply to the proxied request.  Fail.
+        */
+       if (request->proxy && !request->proxy_reply) return RLM_MODULE_FAIL;
+#endif
+
        /*
         *      We get here IF we're not proxying, OR if we've
         *      received the accounting reply from the end server,
index 541a1d3..10a0a3e 100644 (file)
@@ -38,19 +38,19 @@ char *auth_name(char *buf, size_t buflen, REQUEST *request, bool do_cli)
 {
        VALUE_PAIR      *cli;
        VALUE_PAIR      *pair;
-       uint16_t        port = 0;
+       uint32_t        port = 0;       /* RFC 2865 NAS-Port is 4 bytes */
        char const      *tls = "";
 
-       if ((cli = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) == NULL) {
+       if ((cli = fr_pair_find_by_num(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) {
+       if ((pair = fr_pair_find_by_num(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)) {
+               if (fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY)) {
                        tls = " via TLS tunnel";
                } else {
                        tls = " via proxy to virtual server";
@@ -90,7 +90,7 @@ static int rad_authlog(char const *msg, REQUEST *request, int goodpass)
         * Get the correct username based on the configured value
         */
        if (!log_stripped_names) {
-               username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
        } else {
                username = request->username;
        }
@@ -111,7 +111,7 @@ static int rad_authlog(char const *msg, REQUEST *request, int goodpass)
                if (!request->password) {
                        VALUE_PAIR *auth_type;
 
-                       auth_type = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
+                       auth_type = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
                        if (auth_type) {
                                snprintf(clean_password, sizeof(clean_password),
                                         "<via Auth-Type = %s>",
@@ -120,7 +120,7 @@ static int rad_authlog(char const *msg, REQUEST *request, int goodpass)
                        } else {
                                strcpy(clean_password, "<no User-Password attribute>");
                        }
-               } else if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
+               } else if (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
                        strcpy(clean_password, "<CHAP-Password>");
                } else {
                        fr_prints(clean_password, sizeof(clean_password),
@@ -177,16 +177,16 @@ static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
 
        /*
         *      Look for matching check items. We skip the whole lot
-        *      if the authentication type is PW_AUTHTYPE_ACCEPT or
-        *      PW_AUTHTYPE_REJECT.
+        *      if the authentication type is PW_AUTH_TYPE_ACCEPT or
+        *      PW_AUTH_TYPE_REJECT.
         */
-       fr_cursor_init(&cursor, &request->config_items);
+       fr_cursor_init(&cursor, &request->config);
        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++;
 
                RDEBUG2("Found Auth-Type = %s", dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type));
-               if (auth_type == PW_AUTHTYPE_REJECT) {
+               if (auth_type == PW_AUTH_TYPE_REJECT) {
                        RDEBUG2("Auth-Type = Reject, rejecting user");
 
                        return -2;
@@ -197,7 +197,7 @@ static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
         *      Warn if more than one Auth-Type was found, because only the last
         *      one found will actually be used.
         */
-       if ((auth_type_count > 1) && (debug_flag)) {
+       if ((auth_type_count > 1) && (rad_debug_lvl)) {
                RERROR("Warning:  Found %d auth-types on request for user '%s'",
                        auth_type_count, request->username->vp_strvalue);
        }
@@ -207,7 +207,7 @@ static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
         *      rejected in the above loop. So that means it is accepted and we
         *      do no further authentication.
         */
-       if ((auth_type == PW_AUTHTYPE_ACCEPT)
+       if ((auth_type == PW_AUTH_TYPE_ACCEPT)
 #ifdef WITH_PROXY
            || (request->proxy)
 #endif
@@ -223,11 +223,11 @@ static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
         *      been set, and complain if so.
         */
        if (auth_type < 0) {
-               if (pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) {
+               if (fr_pair_find_by_num(request->config, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) {
                        RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Crypt'");
                        RWDEBUG2("Use the PAP module instead");
                }
-               else if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) {
+               else if (fr_pair_find_by_num(request->config, 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");
                }
@@ -292,17 +292,17 @@ int rad_postauth(REQUEST *request)
        VALUE_PAIR *vp;
 
        if (request->reply->code == 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);
+               fr_pair_delete_by_num(&request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
+               vp = pair_make_config("Post-Auth-Type", "Challenge", T_OP_SET);
                if (!vp) return RLM_MODULE_OK;
 
        } else if (request->reply->code == PW_CODE_ACCESS_REJECT) {
-               pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
-               vp = pairmake_config("Post-Auth-Type", "Reject", T_OP_SET);
+               fr_pair_delete_by_num(&request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
+               vp = pair_make_config("Post-Auth-Type", "Reject", T_OP_SET);
                if (!vp) return RLM_MODULE_OK;
 
        } else {
-               vp = pairfind(request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
        }
 
        /*
@@ -380,7 +380,7 @@ int rad_postauth(REQUEST *request)
         *      If we're still accepting the user, say so.
         */
        if (request->reply->code == PW_CODE_ACCESS_ACCEPT) {
-               if ((vp = pairfind(request->packet->vps, PW_MODULE_SUCCESS_MESSAGE, 0, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_num(request->packet->vps, PW_MODULE_SUCCESS_MESSAGE, 0, TAG_ANY)) != NULL) {
                        char msg[MAX_STRING_LEN+12];
 
                        snprintf(msg, sizeof(msg), "Login OK (%s)",
@@ -431,10 +431,10 @@ int rad_authenticate(REQUEST *request)
                 *      accordingly.
                 */
                case PW_CODE_ACCESS_ACCEPT:
-                       tmp = radius_paircreate(request,
-                                               &request->config_items,
+                       tmp = radius_pair_create(request,
+                                               &request->config,
                                                PW_AUTH_TYPE, 0);
-                       if (tmp) tmp->vp_integer = PW_AUTHTYPE_ACCEPT;
+                       if (tmp) tmp->vp_integer = PW_AUTH_TYPE_ACCEPT;
                        goto authenticate;
 
                /*
@@ -472,10 +472,10 @@ int rad_authenticate(REQUEST *request)
         *      Look for, and cache, passwords.
         */
        if (!request->password) {
-               request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+               request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
        }
        if (!request->password) {
-               request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
+               request->password = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
        }
 
        /*
@@ -501,7 +501,7 @@ autz_redo:
        case RLM_MODULE_REJECT:
        case RLM_MODULE_USERLOCK:
        default:
-               if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) {
+               if ((module_msg = fr_pair_find_by_num(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) {
                        char msg[MAX_STRING_LEN + 16];
                        snprintf(msg, sizeof(msg), "Invalid user (%s)",
                                 module_msg->vp_strvalue);
@@ -513,7 +513,7 @@ autz_redo:
                return result;
        }
        if (!autz_retry) {
-               tmp = pairfind(request->config_items, PW_AUTZ_TYPE, 0, TAG_ANY);
+               tmp = fr_pair_find_by_num(request->config, PW_AUTZ_TYPE, 0, TAG_ANY);
                if (tmp) {
                        autz_type = tmp->vp_integer;
                        RDEBUG2("Using Autz-Type %s",
@@ -533,7 +533,7 @@ autz_redo:
 #ifdef WITH_PROXY
            (request->proxy == NULL) &&
 #endif
-           ((tmp = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY)) != NULL)) {
+           ((tmp = fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY)) != NULL)) {
                REALM *realm;
 
                realm = realm_find2(tmp->vp_strvalue);
@@ -570,15 +570,6 @@ authenticate:
        do {
                result = rad_check_password(request);
                if (result > 0) {
-                       /*
-                        *      We presume that the reply has been set by someone.
-                        */
-                       if (request->reply->code == PW_CODE_ACCESS_CHALLENGE) {
-                               fr_state_put_vps(request, request->packet, request->reply);
-
-                       } else {
-                               fr_state_discard(request, request->packet);
-                       }
                        return RLM_MODULE_HANDLED;
                }
 
@@ -595,7 +586,7 @@ authenticate:
                RDEBUG2("Failed to authenticate the user");
                request->reply->code = PW_CODE_ACCESS_REJECT;
 
-               if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL){
+               if ((module_msg = fr_pair_find_by_num(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL){
                        char msg[MAX_STRING_LEN+19];
 
                        snprintf(msg, sizeof(msg), "Login incorrect (%s)",
@@ -608,14 +599,14 @@ authenticate:
                if (request->password) {
                        VERIFY_VP(request->password);
                        /* double check: maybe the secret is wrong? */
-                       if ((debug_flag > 1) && (request->password->da->attr == PW_USER_PASSWORD)) {
+                       if ((rad_debug_lvl > 1) && (request->password->da->attr == PW_USER_PASSWORD)) {
                                uint8_t const *p;
 
                                p = (uint8_t const *) request->password->vp_strvalue;
                                while (*p) {
                                        int size;
 
-                                       size = fr_utf8_char(p);
+                                       size = fr_utf8_char(p, -1);
                                        if (!size) {
                                                RWDEBUG("Unprintable characters in the password.  Double-check the "
                                                        "shared secret on the server and the NAS!");
@@ -629,12 +620,12 @@ authenticate:
 
 #ifdef WITH_SESSION_MGMT
        if (result >= 0 &&
-           (check_item = pairfind(request->config_items, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) {
+           (check_item = fr_pair_find_by_num(request->config, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) {
                int r, session_type = 0;
                char            logstr[1024];
                char            umsg[MAX_STRING_LEN + 1];
 
-               tmp = pairfind(request->config_items, PW_SESSION_TYPE, 0, TAG_ANY);
+               tmp = fr_pair_find_by_num(request->config, PW_SESSION_TYPE, 0, TAG_ANY);
                if (tmp) {
                        session_type = tmp->vp_integer;
                        RDEBUG2("Using Session-Type %s",
@@ -653,7 +644,7 @@ authenticate:
                                /* Multilink attempt. Check if port-limit > simultaneous-use */
                                VALUE_PAIR *port_limit;
 
-                               if ((port_limit = pairfind(request->reply->vps, PW_PORT_LIMIT, 0, TAG_ANY)) != NULL &&
+                               if ((port_limit = fr_pair_find_by_num(request->reply->vps, PW_PORT_LIMIT, 0, TAG_ANY)) != NULL &&
                                        port_limit->vp_integer > check_item->vp_integer){
                                        RDEBUG2("MPP is OK");
                                        mpp_ok = 1;
@@ -673,8 +664,8 @@ authenticate:
                                 *      They're trying to log in too many times.
                                 *      Remove ALL reply attributes.
                                 */
-                               pairfree(&request->reply->vps);
-                               pairmake_reply("Reply-Message", umsg, T_OP_SET);
+                               fr_pair_list_free(&request->reply->vps);
+                               pair_make_reply("Reply-Message", umsg, T_OP_SET);
 
                                snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
                                        check_item->vp_integer,
@@ -715,7 +706,7 @@ int rad_virtual_server(REQUEST *request)
        VALUE_PAIR *vp;
        int result;
 
-       RDEBUG("Virtual server received request");
+       RDEBUG("Virtual server %s received request", request->server);
        rdebug_pair_list(L_DBG_LVL_1, request, request->packet->vps, NULL);
 
        RDEBUG("server %s {", request->server);
@@ -730,8 +721,8 @@ int rad_virtual_server(REQUEST *request)
        result = rad_authenticate(request);
 
        if (request->reply->code == PW_CODE_ACCESS_REJECT) {
-               pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
-               vp = pairmake_config("Post-Auth-Type", "Reject", T_OP_SET);
+               fr_pair_delete_by_num(&request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
+               vp = pair_make_config("Post-Auth-Type", "Reject", T_OP_SET);
                if (vp) rad_postauth(request);
        }
 
index 9aa6bc9..2f38f77 100644 (file)
@@ -76,9 +76,10 @@ void cbtls_info(SSL const *s, int where, int ret)
  *     Fill in our 'info' with TLS data.
  */
 void cbtls_msg(int write_p, int msg_version, int content_type,
-              void const *buf, size_t len,
+              void const *inbuf, size_t len,
               SSL *ssl UNUSED, void *arg)
 {
+       uint8_t const *buf = inbuf;
        tls_session_t *state = (tls_session_t *)arg;
 
        /*
@@ -87,19 +88,19 @@ void cbtls_msg(int write_p, int msg_version, int content_type,
         */
        if (!state) return;
 
-       state->info.origin = (unsigned char)write_p;
-       state->info.content_type = (unsigned char)content_type;
+       state->info.origin = write_p;
+       state->info.content_type = content_type;
        state->info.record_len = len;
        state->info.version = msg_version;
-       state->info.initialized = 1;
+       state->info.initialized = true;
 
        if (content_type == SSL3_RT_ALERT) {
-               state->info.alert_level = ((unsigned char const *)buf)[0];
-               state->info.alert_description = ((unsigned char const *)buf)[1];
+               state->info.alert_level = buf[0];
+               state->info.alert_description = buf[1];
                state->info.handshake_type = 0x00;
 
        } else if (content_type == SSL3_RT_HANDSHAKE) {
-               state->info.handshake_type = ((unsigned char const *)buf)[0];
+               state->info.handshake_type = buf[0];
                state->info.alert_level = 0x00;
                state->info.alert_description = 0x00;
 
index 2ba40f0..d50101e 100644 (file)
@@ -858,7 +858,7 @@ sub cyclades_telnet {
        if ($pr->waitfor(Match => $pr_prompt) == 1) {
                $pr->print('6');
        } else {
-               print LOG "  Error: acessing menu option '6'.\n" if ($debug);
+               print LOG "  Error: accessing menu option '6'.\n" if ($debug);
                $pr->close;
                return 2;
        }
@@ -868,7 +868,7 @@ sub cyclades_telnet {
        if ($pr->waitfor(Match => $pr_prompt) == 1) {
                @list = $pr->cmd(String => '8', Prompt => $endlist);
        } else {
-               print LOG "  Error: acessing menu option '8'.\n" if ($debug);
+               print LOG "  Error: accessing menu option '8'.\n" if ($debug);
                $pr->close;
                return 2;
        }
@@ -976,7 +976,7 @@ sub cyclades_snmp {
 #
 
 #       This routine modified by Dan Halverson <danh@tbc.net>
-#       to suport additional versions of Hiper Arc
+#       to support additional versions of Hiper Arc
 #
 
 $usrm   = '.iso.org.dod.internet.private.enterprises.429';
@@ -1318,7 +1318,7 @@ sub mikrotik_telnet {
   $t->waitfor('/\[.*@.*\] > /');
 
   # It is not possible to get the line numbers etc.
-  # Thus we cant support if simultaneus-use is over 1
+  # Thus we cant support if simultaneous-use is over 1
   # At least I was using pppoe so it wasnt possible.
   $t->print('ppp active print column name detail');
 
index 9e6267c..00229d0 100644 (file)
@@ -1,12 +1,8 @@
 /*
- * client.c    Read clients into memory.
- *
- * Version:     $Id$
- *
- *   This program is free software; you can redistribute it and/or modify
+ *   This program is 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.
+ *   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
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * Copyright 2000,2006  The FreeRADIUS server project
- * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
- * Copyright 2000  Alan DeKok <aland@ox.org>
  */
 
+/**
+ * $Id$
+ * @file main/client.c
+ * @brief Manage clients allowed to communicate with the server.
+ *
+ * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2000,2006 The FreeRADIUS server project
+ * @copyright 2000 Alan DeKok <aland@ox.org>
+ * @copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
+ */
 RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
@@ -69,7 +71,7 @@ void client_free(RADCLIENT *client)
                time_t now;
 
                if (!deleted_clients) {
-                       deleted_clients = fr_fifo_create(1024, (void (*)(void *))client_free);
+                       deleted_clients = fr_fifo_create(NULL, 1024, (void (*)(void *))client_free);
                        if (!deleted_clients) return; /* MEMLEAK */
                }
 
@@ -87,6 +89,8 @@ void client_free(RADCLIENT *client)
                 *      from the queue and delete it.
                 */
                client = fr_fifo_peek(deleted_clients);
+               rad_assert(client != NULL);
+
                if ((client->created + 120) >= now) return;
 
                client = fr_fifo_pop(deleted_clients);
@@ -136,7 +140,7 @@ static int client_num_cmp(void const *one, void const *two)
 /*
  *     Free a RADCLIENT list.
  */
-void clients_free(RADCLIENT_LIST *clients)
+void client_list_free(RADCLIENT_LIST *clients)
 {
        int i;
 
@@ -169,7 +173,7 @@ void clients_free(RADCLIENT_LIST *clients)
 /*
  *     Return a new, initialized, set of clients.
  */
-RADCLIENT_LIST *clients_init(CONF_SECTION *cs)
+RADCLIENT_LIST *client_list_init(CONF_SECTION *cs)
 {
        RADCLIENT_LIST *clients = talloc_zero(cs, RADCLIENT_LIST);
 
@@ -229,6 +233,7 @@ bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
        if (!clients) {
                if (client->server != NULL) {
                        CONF_SECTION *cs;
+                       CONF_SECTION *subcs;
 
                        cs = cf_section_sub_find_name2(main_config.config, "server", client->server);
                        if (!cs) {
@@ -237,30 +242,38 @@ bool client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
                        }
 
                        /*
+                        *      If this server has no "listen" section, add the clients
+                        *      to the global client list.
+                        */
+                       subcs = cf_section_sub_find(cs, "listen");
+                       if (!subcs) goto global_clients;
+
+                       /*
                         *      If the client list already exists, use that.
                         *      Otherwise, create a new client list.
                         */
                        clients = cf_data_find(cs, "clients");
                        if (!clients) {
-                               clients = clients_init(cs);
+                               clients = client_list_init(cs);
                                if (!clients) {
                                        ERROR("Out of memory");
                                        return false;
                                }
 
-                               if (cf_data_add(cs, "clients", clients, (void (*)(void *)) clients_free) < 0) {
+                               if (cf_data_add(cs, "clients", clients, (void (*)(void *)) client_list_free) < 0) {
                                        ERROR("Failed to associate clients with virtual server %s", client->server);
-                                       clients_free(clients);
+                                       client_list_free(clients);
                                        return false;
                                }
                        }
 
                } else {
+               global_clients:
                        /*
                         *      Initialize the global list, if not done already.
                         */
                        if (!root_clients) {
-                               root_clients = clients_init(NULL);
+                               root_clients = client_list_init(NULL);
                                if (!root_clients) return false;
                        }
                        clients = root_clients;
@@ -463,6 +476,7 @@ RADCLIENT *client_find_old(fr_ipaddr_t const *ipaddr)
 }
 
 static fr_ipaddr_t cl_ipaddr;
+static uint32_t cl_netmask;
 static char const *cl_srcipaddr = NULL;
 #ifdef WITH_TCP
 static char const *hs_proto = NULL;
@@ -476,7 +490,7 @@ static CONF_PARSER limit_config[] = {
 
        { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, limit.idle_timeout), "30" },
 
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 #endif
 
@@ -485,6 +499,8 @@ static const CONF_PARSER client_config[] = {
        { "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_netmask), NULL },
+
        { "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &cl_srcipaddr), NULL },
 
        { "require_message_authenticator",  FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), "no" },
@@ -492,7 +508,6 @@ static const CONF_PARSER client_config[] = {
        { "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 },
@@ -511,24 +526,22 @@ static const CONF_PARSER client_config[] = {
        { "rate_limit", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, rate_limit), NULL },
 #endif
 
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
-/*
- *     Create the linked list of clients from the new configuration
- *     type.  This way we don't have to change too much in the other
- *     source-files.
+/** Create the linked list of clients from the new configuration type
+ *
  */
 #ifdef WITH_TLS
-RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, bool tls_required)
+RADCLIENT_LIST *client_list_parse_section(CONF_SECTION *section, bool tls_required)
 #else
-RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_required)
+RADCLIENT_LIST *client_list_parse_section(CONF_SECTION *section, UNUSED bool tls_required)
 #endif
 {
        bool            global = false, in_server = false;
        CONF_SECTION    *cs;
-       RADCLIENT       *c;
-       RADCLIENT_LIST  *clients;
+       RADCLIENT       *c = NULL;
+       RADCLIENT_LIST  *clients = NULL;
 
        /*
         *      Be forgiving.  If there's already a clients, return
@@ -537,29 +550,21 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
        clients = cf_data_find(section, "clients");
        if (clients) return clients;
 
-       clients = clients_init(section);
+       clients = client_list_init(section);
        if (!clients) return NULL;
 
        if (cf_top_section(section) == section) global = true;
 
        if (strcmp("server", cf_section_name1(section)) == 0) in_server = true;
 
-       /*
-        *      Associate the clients structure with the section.
-        */
-       if (cf_data_add(section, "clients", clients, NULL) < 0) {
-               cf_log_err_cs(section,
-                          "Failed to associate clients with section %s",
-                      cf_section_name1(section));
-               clients_free(clients);
-               return NULL;
-       }
-
        for (cs = cf_subsection_find_next(section, NULL, "client");
-            cs != NULL;
+            cs;
             cs = cf_subsection_find_next(section, cs, "client")) {
                c = client_afrom_cs(cs, cs, in_server, false);
                if (!c) {
+               error:
+                       client_free(c);
+                       client_list_free(clients);
                        return NULL;
                }
 
@@ -570,9 +575,7 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
                 */
                if (tls_required != c->tls_required) {
                        cf_log_err_cs(cs, "Client does not have the same TLS configuration as the listener");
-                       client_free(c);
-                       clients_free(clients);
-                       return NULL;
+                       goto error;
                }
 #endif
 
@@ -584,12 +587,12 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
 #ifdef WITH_DYNAMIC_CLIENTS
 #ifdef HAVE_DIRENT_H
                if (c->client_server) {
-                       char const *value;
-                       CONF_PAIR *cp;
+                       char const      *value;
+                       CONF_PAIR       *cp;
                        DIR             *dir;
                        struct dirent   *dp;
-                       struct stat stat_buf;
-                       char buf2[2048];
+                       struct stat     stat_buf;
+                       char            buf2[2048];
 
                        /*
                         *      Find the directory where individual
@@ -600,10 +603,8 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
 
                        value = cf_pair_value(cp);
                        if (!value) {
-                               cf_log_err_cs(cs,
-                                          "The \"directory\" entry must not be empty");
-                               client_free(c);
-                               return NULL;
+                               cf_log_err_cs(cs, "The \"directory\" entry must not be empty");
+                               goto error;
                        }
 
                        DEBUG("including dynamic clients in %s", value);
@@ -611,8 +612,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, fr_syserror(errno));
-                               client_free(c);
-                               return NULL;
+                               goto error;
                        }
 
                        /*
@@ -632,33 +632,27 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
                                            isdigit((int)*p) ||
                                            (*p == ':') ||
                                            (*p == '.')) continue;
-                                               break;
+                                       break;
                                }
                                if (*p != '\0') continue;
 
-                               snprintf(buf2, sizeof(buf2), "%s/%s",
-                                        value, dp->d_name);
+                               snprintf(buf2, sizeof(buf2), "%s/%s", value, dp->d_name);
 
-                               if ((stat(buf2, &stat_buf) != 0) ||
-                                   S_ISDIR(stat_buf.st_mode)) continue;
+                               if ((stat(buf2, &stat_buf) != 0) || S_ISDIR(stat_buf.st_mode)) continue;
 
                                dc = client_read(buf2, in_server, true);
                                if (!dc) {
-                                       cf_log_err_cs(cs,
-                                                  "Failed reading client file \"%s\"", buf2);
-                                       client_free(c);
+                                       cf_log_err_cs(cs, "Failed reading client file \"%s\"", buf2);
                                        closedir(dir);
-                                       return NULL;
+                                       goto error;
                                }
 
                                /*
                                 *      Validate, and add to the list.
                                 */
                                if (!client_add_dynamic(clients, c, dc)) {
-
-                                       client_free(c);
                                        closedir(dir);
-                                       return NULL;
+                                       goto error;
                                }
                        } /* loop over the directory */
                        closedir(dir);
@@ -668,23 +662,27 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
        add_client:
 #endif /* WITH_DYNAMIC_CLIENTS */
                if (!client_add(clients, c)) {
-                       cf_log_err_cs(cs,
-                                  "Failed to add client %s",
-                                  cf_section_name2(cs));
-                       client_free(c);
-                       return NULL;
+                       cf_log_err_cs(cs, "Failed to add client %s", cf_section_name2(cs));
+                       goto error;
                }
 
        }
 
        /*
+        *      Associate the clients structure with the section.
+        */
+       if (cf_data_add(section, "clients", clients, NULL) < 0) {
+               cf_log_err_cs(section, "Failed to associate clients with section %s", cf_section_name1(section));
+               client_list_free(clients);
+               return NULL;
+       }
+
+       /*
         *      Replace the global list of clients with the new one.
         *      The old one is still referenced from the original
         *      configuration, and will be freed when that is freed.
         */
-       if (global) {
-               root_clients = clients;
-       }
+       if (global) root_clients = clients;
 
        return clients;
 }
@@ -708,7 +706,7 @@ static const CONF_PARSER dynamic_config[] = {
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 /** Add a dynamic client
@@ -763,13 +761,102 @@ error:
        return false;
 }
 
+/** Create a client CONF_SECTION using a mapping section to map values from a result set to client attributes
+ *
+ * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too.
+ *
+ * @note Caller should free CONF_SECTION passed in as out, on error.
+ *      Contents of that section will be in an undefined state.
+ *
+ * @param[in,out] out Section to perform mapping on. Either the root of the client config, or a parent section
+ *     (when this function is called recursively).
+ *     Should be alloced with cf_section_alloc, or if there's a separate template section, the
+ *     result of calling cf_section_dup on that section.
+ * @param[in] map section.
+ * @param[in] func to call to retrieve CONF_PAIR values. Must return a talloced buffer containing the value.
+ * @param[in] data to pass to func, usually a result pointer.
+ * @return 0 on success else -1 on error.
+ */
+int client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data)
+{
+       CONF_ITEM const *ci;
+
+       for (ci = cf_item_find_next(map, NULL);
+            ci != NULL;
+            ci = cf_item_find_next(map, ci)) {
+               CONF_PAIR const *cp;
+               CONF_PAIR *old;
+               char *value;
+               char const *attr;
+
+               /*
+                *      Recursively process map subsection
+                */
+               if (cf_item_is_section(ci)) {
+                       CONF_SECTION *cs, *cc;
+
+                       cs = cf_item_to_section(ci);
+                       /*
+                        *      Use pre-existing section or alloc a new one
+                        */
+                       cc = cf_section_sub_find_name2(out, cf_section_name1(cs), cf_section_name2(cs));
+                       if (!cc) {
+                               cc = cf_section_alloc(out, cf_section_name1(cs), cf_section_name2(cs));
+                               cf_section_add(out, cc);
+                               if (!cc) return -1;
+                       }
+
+                       if (client_map_section(cc, cs, func, data) < 0) return -1;
+                       continue;
+               }
+
+               cp = cf_item_to_pair(ci);
+               attr = cf_pair_attr(cp);
+
+               /*
+                *      The callback can return 0 (success) and not provide a value
+                *      in which case we skip the mapping pair.
+                *
+                *      Or return -1 in which case we error out.
+                */
+               if (func(&value, cp, data) < 0) {
+                       cf_log_err_cs(out, "Failed performing mapping \"%s\" = \"%s\"", attr, cf_pair_value(cp));
+                       return -1;
+               }
+               if (!value) continue;
+
+               /*
+                *      Replace an existing CONF_PAIR
+                */
+               old = cf_pair_find(out, attr);
+               if (old) {
+                       cf_pair_replace(out, old, value);
+                       talloc_free(value);
+                       continue;
+               }
+
+               /*
+                *      ...or add a new CONF_PAIR
+                */
+               cp = cf_pair_alloc(out, attr, value, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
+               if (!cp) {
+                       cf_log_err_cs(out, "Failed allocing pair \"%s\" = \"%s\"", attr, value);
+                       talloc_free(value);
+                       return -1;
+               }
+               talloc_free(value);
+               cf_item_add(out, cf_pair_to_item(cp));
+       }
+
+       return 0;
+}
 
 /** Allocate a new client from a config section
  *
  * @param ctx to allocate new clients in.
  * @param cs to process as a client.
  * @param in_server Whether the client should belong to a specific virtual server.
- * @param with_coa If true and coa_server or coa_pool aren't specified automatically
+ * @param with_coa If true and coa_server or coa_pool aren't specified automatically,
  *     create a coa home_server section and add it to the client CONF_SECTION.
  * @return new RADCLIENT struct.
  */
@@ -791,6 +878,8 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
        c->cs = cs;
 
        memset(&cl_ipaddr, 0, sizeof(cl_ipaddr));
+       cl_netmask = 255;
+
        if (cf_section_parse(cs, c, client_config) < 0) {
                cf_log_err_cs(cs, "Error parsing client section");
        error:
@@ -812,6 +901,20 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
        }
 
        /*
+        *      Allow the old method to specify "netmask".  Just using "1.2.3.4" means it's a /32.
+        */
+       if (cl_netmask != 255) {
+               if ((cl_ipaddr.prefix != cl_netmask) &&
+                   (((cl_ipaddr.af == AF_INET) && cl_ipaddr.prefix != 32) ||
+                    ((cl_ipaddr.af == AF_INET6) && cl_ipaddr.prefix != 128))) {
+                       cf_log_err_cs(cs, "Clients cannot use 'ipaddr/mask' and 'netmask' at the same time.");
+                       goto error;
+               }
+
+               cl_ipaddr.prefix = cl_netmask;
+       }
+
+       /*
         *      Newer style client definitions with either ipaddr or ipaddr6
         *      config items.
         */
@@ -837,9 +940,24 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
         *      No "ipaddr" or "ipv6addr", use old-style "client <ipaddr> {" syntax.
         */
        } else {
-               cf_log_err_cs(cs, "No 'ipaddr' or 'ipv4addr' or 'ipv6addr' configuration "
-                             "directive found in client %s", name2);
-               goto error;
+               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");
+
+#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, -1, AF_UNSPEC, 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);
        }
 
        c->proto = IPPROTO_UDP;
@@ -1002,7 +1120,7 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
                         *      realm_config_t without a mutex, by one of the
                         *      workers, would be bad.
                         */
-                       home = home_server_afrom_cs(c, NULL, server);
+                       home = home_server_afrom_cs(NULL, NULL, server);
                        if (!home) {
                                talloc_free(server);
                                goto error;
@@ -1031,7 +1149,9 @@ done_coa:
        return c;
 }
 
-/** Add a client from a result set (LDAP, SQL, et al)
+/** Add a client from a result set (SQL)
+ *
+ * @todo This function should die. SQL should use client_afrom_cs.
  *
  * @param ctx Talloc context.
  * @param identifier Client IP Address / IPv4 subnet / IPv6 subnet / FQDN.
@@ -1053,7 +1173,7 @@ RADCLIENT *client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char cons
 
        c = talloc_zero(ctx, RADCLIENT);
 
-       if (fr_pton(&c->ipaddr, identifier, -1, true) < 0) {
+       if (fr_pton(&c->ipaddr, identifier, -1, AF_UNSPEC, true) < 0) {
                ERROR("%s", fr_strerror());
                talloc_free(c);
 
@@ -1086,22 +1206,27 @@ RADCLIENT *client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char cons
  */
 RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
 {
-       int i, *pi;
-       char **p;
-       RADCLIENT *c;
-       char buffer[128];
+       static int      cnt;
+       int             i, *pi;
+       char            **p;
+       RADCLIENT       *c;
+       CONF_PAIR       *cp = NULL;
+       char            buffer[128];
 
-       vp_cursor_t cursor;
-       VALUE_PAIR *vp = NULL;
+       vp_cursor_t     cursor;
+       VALUE_PAIR      *vp = NULL;
 
        if (!clients || !request) return NULL;
 
+       snprintf(buffer, sizeof(buffer), "dynamic%i", cnt++);
+
        c = talloc_zero(clients, RADCLIENT);
-       c->cs = request->client->cs;
+       c->cs = cf_section_alloc(NULL, "client", buffer);
+       talloc_steal(c, c->cs);
        c->ipaddr.af = AF_UNSPEC;
        c->src_ipaddr.af = AF_UNSPEC;
 
-       fr_cursor_init(&cursor, &request->config_items);
+       fr_cursor_init(&cursor, &request->config);
 
        RDEBUG2("Converting control list to client fields");
        RINDENT();
@@ -1138,21 +1263,22 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
                /*
                 *      Freed at the same time as the vp.
                 */
-               if (RDEBUG_ENABLED2) strvalue = vp_aprints_value(vp, vp, '\'');
+               strvalue = vp_aprints_value(vp, vp, '\'');
 
                switch (dynamic_config[i].type) {
                case PW_TYPE_IPV4_ADDR:
                        if (da->attr == PW_FREERADIUS_CLIENT_IP_ADDRESS) {
-                               RDEBUG2("ipaddr = %s", strvalue);
                                c->ipaddr.af = AF_INET;
                                c->ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                                c->ipaddr.prefix = 32;
+                               cp = cf_pair_alloc(c->cs, "ipv4addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
                        } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS) {
 #ifdef WITH_UDPFROMTO
                                RDEBUG2("src_ipaddr = %s", strvalue);
                                c->src_ipaddr.af = AF_INET;
                                c->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                                c->src_ipaddr.prefix = 32;
+                               cp = cf_pair_alloc(c->cs, "src_ipaddr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
 #else
                                RWARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IP-Address");
 #endif
@@ -1162,16 +1288,16 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
 
                case PW_TYPE_IPV6_ADDR:
                        if (da->attr == PW_FREERADIUS_CLIENT_IPV6_ADDRESS) {
-                               RDEBUG2("ipaddr = %s", strvalue);
                                c->ipaddr.af = AF_INET6;
                                c->ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
                                c->ipaddr.prefix = 128;
+                               cp = cf_pair_alloc(c->cs, "ipv6addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
                        } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) {
 #ifdef WITH_UDPFROMTO
-                               RDEBUG2("src_ipaddr = %s", strvalue);
                                c->src_ipaddr.af = AF_INET6;
                                c->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
                                c->src_ipaddr.prefix = 128;
+                               cp = cf_pair_alloc(c->cs, "src_addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
 #else
                                RWARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IPv6-Address");
 #endif
@@ -1181,53 +1307,77 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
 
                case PW_TYPE_IPV4_PREFIX:
                        if (da->attr == PW_FREERADIUS_CLIENT_IP_PREFIX) {
-                               RDEBUG2("ipaddr = %s", strvalue);
                                c->ipaddr.af = AF_INET;
                                memcpy(&c->ipaddr.ipaddr.ip4addr, &vp->vp_ipv4prefix[2],
                                       sizeof(c->ipaddr.ipaddr.ip4addr.s_addr));
                                fr_ipaddr_mask(&c->ipaddr, (vp->vp_ipv4prefix[1] & 0x3f));
+                               cp = cf_pair_alloc(c->cs, "ipv4addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
                        }
 
                        break;
 
                case PW_TYPE_IPV6_PREFIX:
                        if (da->attr == PW_FREERADIUS_CLIENT_IPV6_PREFIX) {
-                               RDEBUG2("ipaddr = %s", strvalue);
                                c->ipaddr.af = AF_INET6;
-                               memcpy(&c->ipaddr.ipaddr.ip6addr, &vp->vp_ipv6prefix[2], sizeof(c->ipaddr.ipaddr.ip6addr));
+                               memcpy(&c->ipaddr.ipaddr.ip6addr, &vp->vp_ipv6prefix[2],
+                                      sizeof(c->ipaddr.ipaddr.ip6addr));
                                fr_ipaddr_mask(&c->ipaddr, vp->vp_ipv6prefix[1]);
+                               cp = cf_pair_alloc(c->cs, "ipv6addr", strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
                        }
 
                        break;
 
                case PW_TYPE_STRING:
                {
-                       CONF_PARSER const *cp;
+                       CONF_PARSER const *parse;
 
+                       /*
+                        *      Cache pointer to CONF_PAIR buffer in RADCLIENT struct
+                        */
                        p = (char **) ((char *) c + dynamic_config[i].offset);
-                       if (*p) talloc_free(*p);
-                       if (vp->vp_strvalue[0]) {
-                               *p = talloc_typed_strdup(c->cs, vp->vp_strvalue);
-                       } else {
-                               *p = NULL;
-                       }
+                       if (*p) TALLOC_FREE(*p);
+                       if (!vp->vp_strvalue[0]) break;
 
-                       if (RDEBUG_ENABLED2) for (cp = client_config; cp->name; cp++) {
-                               if (cp->offset == dynamic_config[i].offset) RDEBUG2("%s = '%s'", cp->name, strvalue);
+                       /*
+                        *      We could reuse the CONF_PAIR buff, this just keeps things
+                        *      consistent between client_afrom_cs, and client_afrom_query.
+                        */
+                       *p = talloc_strdup(c, strvalue);
+
+                       /*
+                        *      This is fairly nasty... In order to figure out the CONF_PAIR
+                        *      name associated with a field, find offsets that match between
+                        *      the dynamic_config CONF_PARSER table, and the client_config
+                        *      CONF_PARSER table.
+                        *
+                        *      This is so that things that expect to find CONF_PAIRs in the
+                        *      client CONF_SECTION for fields like 'nas_type' can.
+                        */
+                       for (parse = client_config; parse->name; parse++) {
+                               if (parse->offset == dynamic_config[i].offset) break;
                        }
+                       rad_assert(parse);
+
+                       cp = cf_pair_alloc(c->cs, parse->name, strvalue, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
                }
                        break;
 
                case PW_TYPE_BOOLEAN:
                {
-                       CONF_PARSER const *cp;
+                       CONF_PARSER const *parse;
 
                        pi = (int *) ((bool *) ((char *) c + dynamic_config[i].offset));
                        *pi = vp->vp_integer;
 
-                       if (RDEBUG_ENABLED2) for (cp = client_config; cp->name; cp++) {
-                               if (cp->offset == dynamic_config[i].offset) RDEBUG2("%s = %s", cp->name, strvalue);
+                       /*
+                        *      Same nastiness as above.
+                        */
+                       for (parse = client_config; parse->name; parse++) {
+                               if (parse->offset == dynamic_config[i].offset) break;
                        }
+                       rad_assert(parse);
+
+                       cp = cf_pair_alloc(c->cs, parse->name, strvalue, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
                }
                        break;
 
@@ -1235,6 +1385,18 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
                        goto error;
                }
 
+               if (!cp) {
+                       RERROR("Error creating equivalent conf pair for %s", vp->da->name);
+                       goto error;
+               }
+
+               if (cf_pair_attr_type(cp) == T_SINGLE_QUOTED_STRING) {
+                       RDEBUG2("%s = '%s'", cf_pair_attr(cp), cf_pair_value(cp));
+               } else {
+                       RDEBUG2("%s = %s", cf_pair_attr(cp), cf_pair_value(cp));
+               }
+               cf_pair_add(c->cs, cp);
+
                talloc_free(vp);
        }
 
@@ -1242,8 +1404,6 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
        vp = fr_cursor_remove(&cursor);
        if (vp) {
                do {
-                       CONF_PAIR *cp;
-                       CONF_ITEM *ci;
                        char *value;
 
                        value = vp_aprints_value(vp, vp, '\'');
@@ -1252,11 +1412,16 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
                                goto error;
                        }
 
-                       RDEBUG2("%s = '%s'", vp->da->name, value);
-
-                       cp = cf_pair_alloc(c->cs, vp->da->name, value, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
-                       ci = cf_pair_to_item(cp);
-                       cf_item_add(c->cs, ci);
+                       if (vp->da->type == PW_TYPE_STRING) {
+                               RDEBUG2("%s = '%s'", vp->da->name, value);
+                               cp = cf_pair_alloc(c->cs, vp->da->name, value, T_OP_SET,
+                                                  T_BARE_WORD, T_SINGLE_QUOTED_STRING);
+                       } else {
+                               RDEBUG2("%s = %s", vp->da->name, value);
+                               cp = cf_pair_alloc(c->cs, vp->da->name, value, T_OP_SET,
+                                                  T_BARE_WORD, T_BARE_WORD);
+                       }
+                       cf_pair_add(c->cs, cp);
 
                        talloc_free(vp);
                } while ((vp = fr_cursor_remove(&cursor)));
index a6968d2..099bb78 100644 (file)
@@ -53,6 +53,9 @@ typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]);
 #define FR_READ  (1)
 #define FR_WRITE (2)
 
+#define CMD_FAIL FR_CHANNEL_FAIL
+#define CMD_OK   FR_CHANNEL_SUCCESS
+
 struct fr_command_table_t {
        char const *command;
        int mode;               /* read/write */
@@ -103,7 +106,7 @@ static const CONF_PARSER command_config[] = {
        { "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 },
        { "peercred", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_command_socket_t, peercred), "yes" },
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 static FR_NAME_NUMBER mode_names[] = {
@@ -192,6 +195,8 @@ static int fr_server_domain_socket_peercred(char const *path, uid_t UNUSED uid,
                 *      FIXME: Check the enclosing directory?
                 */
        } else {                /* it exists */
+               int client_fd;
+
                if (!S_ISREG(buf.st_mode)
 #ifdef S_ISSOCK
                    && !S_ISSOCK(buf.st_mode)
@@ -211,9 +216,20 @@ static int fr_server_domain_socket_peercred(char const *path, uid_t UNUSED uid,
                        return -1;
                }
 
+               /*
+                *      Check if a server is already listening on the
+                *      socket?
+                */
+               client_fd = fr_socket_client_unix(path, false);
+               if (client_fd >= 0) {
+                       fr_strerror_printf("Control socket '%s' is already in use", path);
+                       close(client_fd);
+                       close(sockfd);
+                       return -1;
+               }
+
                if (unlink(path) < 0) {
-                      fr_strerror_printf("Failed to delete %s: %s",
-                            path, fr_syserror(errno));
+                      fr_strerror_printf("Failed to delete %s: %s", path, fr_syserror(errno));
                       close(sockfd);
                       return -1;
                }
@@ -450,6 +466,7 @@ static int fr_server_domain_socket_perm(char const *path, uid_t uid, gid_t gid)
         */
        } else {
                int ret;
+               int client_fd;
 
                ret = fstat(dir_fd, &st);
                if (ret < 0) {
@@ -500,7 +517,19 @@ static int fr_server_domain_socket_perm(char const *path, uid_t uid, gid_t gid)
                                           "permissions are %s (%s)", str_need, oct_need, str_have, oct_have);
                        goto error;
                }
+
+               /*
+                *      Check if a server is already listening on the
+                *      socket?
+                */
+               client_fd = fr_socket_client_unix(path, false);
+               if (client_fd >= 0) {
+                       fr_strerror_printf("Control socket '%s' is already in use", path);
+                       close(client_fd);
+                       return -1;
+               }
        }
+
        name = strrchr(path, FR_DIR_SEP);
        if (!name) {
                fr_strerror_printf("Can't determine socket name");
@@ -540,7 +569,6 @@ static int fr_server_domain_socket_perm(char const *path, uid_t uid, gid_t gid)
                if (uid != (uid_t)-1) rad_seuid(euid);
                if (gid != (gid_t)-1) rad_segid(egid);
                close(sock_fd);
-               sock_fd = -1;
 
                goto error;
        }
@@ -715,7 +743,7 @@ static int command_hup(rad_listen_t *listener, int argc, char *argv[])
 
        if (argc == 0) {
                radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
-               return 1;
+               return CMD_OK;
        }
 
        /*
@@ -723,34 +751,34 @@ static int command_hup(rad_listen_t *listener, int argc, char *argv[])
         */
        if (strcmp(argv[0], "main.log") == 0) {
                hup_logfile();
-               return 1;
+               return CMD_OK;
        }
 
        cs = cf_section_find("modules");
-       if (!cs) return 0;
+       if (!cs) return CMD_FAIL;
 
-       mi = find_module_instance(cs, argv[0], false);
+       mi = module_find(cs, argv[0]);
        if (!mi) {
                cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
-               return 0;
+               return CMD_FAIL;
        }
 
        if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
                cprintf_error(listener, "Module %s cannot be hup'd\n",
                        argv[0]);
-               return 0;
+               return CMD_FAIL;
        }
 
        if (!module_hup_module(mi->cs, mi, time(NULL))) {
                cprintf_error(listener, "Failed to reload module\n");
-               return 0;
+               return CMD_FAIL;
        }
 
        snprintf(buffer, sizeof(buffer), "modules.%s.hup",
                 cf_section_name1(mi->cs));
        exec_trigger(NULL, mi->cs, buffer, true);
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 static int command_terminate(UNUSED rad_listen_t *listener,
@@ -758,7 +786,7 @@ static int command_terminate(UNUSED rad_listen_t *listener,
 {
        radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 static int command_uptime(rad_listen_t *listener,
@@ -769,7 +797,7 @@ static int command_uptime(rad_listen_t *listener,
        CTIME_R(&fr_start_time, buffer, sizeof(buffer));
        cprintf(listener, "Up since %s", buffer); /* no \r\n */
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 static int command_show_config(rad_listen_t *listener, int argc, char *argv[])
@@ -780,21 +808,21 @@ static int command_show_config(rad_listen_t *listener, int argc, char *argv[])
 
        if (argc != 1) {
                cprintf_error(listener, "No path was given\n");
-               return 0;
+               return CMD_FAIL;
        }
 
        ci = cf_reference_item(main_config.config, main_config.config, argv[0]);
-       if (!ci) return 0;
+       if (!ci) return CMD_FAIL;
 
-       if (!cf_item_is_pair(ci)) return 0;
+       if (!cf_item_is_pair(ci)) return CMD_FAIL;
 
        cp = cf_item_to_pair(ci);
        value = cf_pair_value(cp);
-       if (!value) return 0;
+       if (!value) return CMD_FAIL;
 
        cprintf(listener, "%s\n", value);
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
@@ -903,24 +931,24 @@ static int command_show_module_config(rad_listen_t *listener, int argc, char *ar
 
        if (argc != 1) {
                cprintf_error(listener, "No module name was given\n");
-               return 0;
+               return CMD_FAIL;
        }
 
        cs = cf_section_find("modules");
-       if (!cs) return 0;
+       if (!cs) return CMD_FAIL;
 
-       mi = find_module_instance(cs, argv[0], false);
+       mi = module_find(cs, argv[0]);
        if (!mi) {
                cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
-               return 0;
+               return CMD_FAIL;
        }
 
        cprint_conf_parser(listener, 0, mi->cs, mi->insthandle);
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
-static char const *method_names[RLM_COMPONENT_COUNT] = {
+static char const *method_names[MOD_COUNT] = {
        "authenticate",
        "authorize",
        "preacct",
@@ -941,25 +969,25 @@ static int command_show_module_methods(rad_listen_t *listener, int argc, char *a
 
        if (argc != 1) {
                cprintf_error(listener, "No module name was given\n");
-               return 0;
+               return CMD_FAIL;
        }
 
        cs = cf_section_find("modules");
-       if (!cs) return 0;
+       if (!cs) return CMD_FAIL;
 
-       mi = find_module_instance(cs, argv[0], false);
+       mi = module_find(cs, argv[0]);
        if (!mi) {
                cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
-               return 0;
+               return CMD_FAIL;
        }
 
        mod = mi->entry->module;
 
-       for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
+       for (i = 0; i < MOD_COUNT; i++) {
                if (mod->methods[i]) cprintf(listener, "%s\n", method_names[i]);
        }
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 
@@ -971,16 +999,16 @@ static int command_show_module_flags(rad_listen_t *listener, int argc, char *arg
 
        if (argc != 1) {
                cprintf_error(listener, "No module name was given\n");
-               return 0;
+               return CMD_FAIL;
        }
 
        cs = cf_section_find("modules");
-       if (!cs) return 0;
+       if (!cs) return CMD_FAIL;
 
-       mi = find_module_instance(cs, argv[0], false);
+       mi = module_find(cs, argv[0]);
        if (!mi) {
                cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
-               return 0;
+               return CMD_FAIL;
        }
 
        mod = mi->entry->module;
@@ -988,15 +1016,10 @@ static int command_show_module_flags(rad_listen_t *listener, int argc, char *arg
        if ((mod->type & RLM_TYPE_THREAD_UNSAFE) != 0)
                cprintf(listener, "thread-unsafe\n");
 
-
-       if ((mod->type & RLM_TYPE_CHECK_CONFIG_UNSAFE) != 0)
-               cprintf(listener, "no-check-config\n");
-
-
        if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
                cprintf(listener, "reload-on-hup\n");
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 static int command_show_module_status(rad_listen_t *listener, int argc, char *argv[])
@@ -1006,16 +1029,16 @@ static int command_show_module_status(rad_listen_t *listener, int argc, char *ar
 
        if (argc != 1) {
                cprintf_error(listener, "No module name was given\n");
-               return 0;
+               return CMD_FAIL;
        }
 
        cs = cf_section_find("modules");
-       if (!cs) return 0;
+       if (!cs) return CMD_FAIL;
 
-       mi = find_module_instance(cs, argv[0], false);
+       mi = module_find(cs, argv[0]);
        if (!mi) {
                cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
-               return 0;
+               return CMD_FAIL;
        }
 
        if (!mi->force) {
@@ -1025,7 +1048,7 @@ static int command_show_module_status(rad_listen_t *listener, int argc, char *ar
        }
 
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 
@@ -1037,7 +1060,7 @@ static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED
        CONF_SECTION *cs, *subcs;
 
        cs = cf_section_find("modules");
-       if (!cs) return 0;
+       if (!cs) return CMD_FAIL;
 
        subcs = NULL;
        while ((subcs = cf_subsection_find_next(cs, subcs, NULL)) != NULL) {
@@ -1047,19 +1070,19 @@ static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED
                module_instance_t *mi;
 
                if (name2) {
-                       mi = find_module_instance(cs, name2, false);
+                       mi = module_find(cs, name2);
                        if (!mi) continue;
 
                        cprintf(listener, "%s (%s)\n", name2, name1);
                } else {
-                       mi = find_module_instance(cs, name1, false);
+                       mi = module_find(cs, name1);
                        if (!mi) continue;
 
                        cprintf(listener, "%s\n", name1);
                }
        }
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 #ifdef WITH_PROXY
@@ -1086,6 +1109,9 @@ static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UN
                } else if (home->type == HOME_TYPE_ACCT) {
                        type = "acct";
 
+               } else if (home->type == HOME_TYPE_AUTH_ACCT) {
+                       type = "auth+acct";
+
 #ifdef WITH_COA
                } else if (home->type == HOME_TYPE_COA) {
                        type = "coa";
@@ -1140,7 +1166,7 @@ static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UN
                        home->currently_outstanding);
        }
 
-       return 0;
+       return CMD_OK;
 }
 #endif
 
@@ -1166,14 +1192,14 @@ static int command_show_clients(rad_listen_t *listener, UNUSED int argc, UNUSED
                }
        }
 
-       return 0;
+       return CMD_OK;
 }
 
 
 static int command_show_version(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
 {
        cprintf(listener, "%s\n", radiusd_version);
-       return 1;
+       return CMD_OK;
 }
 
 static int command_debug_level(rad_listen_t *listener, int argc, char *argv[])
@@ -1191,16 +1217,16 @@ static int command_debug_level(rad_listen_t *listener, int argc, char *argv[])
                return -1;
        }
 
-       fr_debug_flag = debug_flag = number;
+       fr_debug_lvl = rad_debug_lvl = number;
 
-       return 0;
+       return CMD_OK;
 }
 
 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.dst == L_DST_STDOUT) {
+       if (rad_debug_lvl && default_log.dst == L_DST_STDOUT) {
                cprintf_error(listener, "Cannot redirect debug logs to a file when already in debugging mode.\n");
                return -1;
        }
@@ -1211,7 +1237,7 @@ static int command_debug_file(rad_listen_t *listener, int argc, char *argv[])
 
        default_log.debug_file = NULL;
 
-       if (argc == 0) return 0;
+       if (argc == 0) return CMD_OK;
 
        /*
         *      This looks weird, but it's here to avoid locking
@@ -1227,7 +1253,7 @@ static int command_debug_file(rad_listen_t *listener, int argc, char *argv[])
 
        default_log.debug_file = &debug_log_file_buffer[0];
 
-       return 0;
+       return CMD_OK;
 }
 
 extern fr_cond_t *debug_condition;
@@ -1243,9 +1269,9 @@ static int command_debug_condition(rad_listen_t *listener, int argc, char *argv[
         *      Disable it.
         */
        if (argc == 0) {
-               talloc_free(debug_condition);
+               TALLOC_FREE(debug_condition);
                debug_condition = NULL;
-               return 0;
+               return CMD_OK;
        }
 
        if (!((argc == 1) &&
@@ -1317,11 +1343,11 @@ static int command_debug_condition(rad_listen_t *listener, int argc, char *argv[
                ERROR("%s", p);
                ERROR("%s^ %s", spaces, error);
 
-               cprintf(listener, "Parse error in condition \"%s\": %s\n", p, error);
+               cprintf_error(listener, "Parse error in condition \"%s\": %s\n", p, error);
 
                talloc_free(spaces);
                talloc_free(text);
-               return 0;
+               return CMD_FAIL;
        }
 
        /*
@@ -1330,10 +1356,10 @@ static int command_debug_condition(rad_listen_t *listener, int argc, char *argv[
         *      This is thread-safe because the condition is evaluated
         *      in the main server thread, along with this code.
         */
-       talloc_free(debug_condition);
+       TALLOC_FREE(debug_condition);
        debug_condition = new_condition;
 
-       return 0;
+       return CMD_OK;
 }
 
 static int command_show_debug_condition(rad_listen_t *listener,
@@ -1341,30 +1367,33 @@ static int command_show_debug_condition(rad_listen_t *listener,
 {
        char buffer[1024];
 
-       if (!debug_condition) return 0;
+       if (!debug_condition) {
+               cprintf(listener, "\n");
+               return CMD_OK;
+       }
 
        fr_cond_sprint(buffer, sizeof(buffer), debug_condition);
 
        cprintf(listener, "%s\n", buffer);
-       return 0;
+       return CMD_OK;
 }
 
 
 static int command_show_debug_file(rad_listen_t *listener,
                                        UNUSED int argc, UNUSED char *argv[])
 {
-       if (!default_log.debug_file) return 0;
+       if (!default_log.debug_file) return CMD_FAIL;
 
        cprintf(listener, "%s\n", default_log.debug_file);
-       return 0;
+       return CMD_OK;
 }
 
 
 static int command_show_debug_level(rad_listen_t *listener,
                                        UNUSED int argc, UNUSED char *argv[])
 {
-       cprintf(listener, "%d\n", debug_flag);
-       return 0;
+       cprintf(listener, "%d\n", rad_debug_lvl);
+       return CMD_OK;
 }
 
 
@@ -1497,7 +1526,8 @@ static home_server_t *get_home_server(rad_listen_t *listener, int argc,
                return NULL;
        }
 
-       *last = myarg;
+       if (last) *last = myarg;
+
        return home;
 }
 
@@ -1508,12 +1538,12 @@ static int command_set_home_server_state(rad_listen_t *listener, int argc, char
 
        if (argc < 3) {
                cprintf_error(listener, "Must specify <ipaddr> <port> [udp|tcp] <state>\n");
-               return 0;
+               return CMD_FAIL;
        }
 
        home = get_home_server(listener, argc, argv, &last);
        if (!home) {
-               return 0;
+               return CMD_FAIL;
        }
 
        if (strcmp(argv[last], "alive") == 0) {
@@ -1527,10 +1557,10 @@ static int command_set_home_server_state(rad_listen_t *listener, int argc, char
 
        } else {
                cprintf_error(listener, "Unknown state \"%s\"\n", argv[last]);
-               return 0;
+               return CMD_FAIL;
        }
 
-       return 1;
+       return CMD_OK;
 }
 
 static int command_show_home_server_state(rad_listen_t *listener, int argc, char *argv[])
@@ -1538,9 +1568,7 @@ static int command_show_home_server_state(rad_listen_t *listener, int argc, char
        home_server_t *home;
 
        home = get_home_server(listener, argc, argv, NULL);
-       if (!home) {
-               return 0;
-       }
+       if (!home) return CMD_FAIL;
 
        switch (home->state) {
        case HOME_STATE_ALIVE:
@@ -1564,7 +1592,7 @@ static int command_show_home_server_state(rad_listen_t *listener, int argc, char
                break;
        }
 
-       return 1;
+       return CMD_OK;
 }
 #endif
 
@@ -1605,7 +1633,7 @@ static int null_socket_send(UNUSED rad_listen_t *listener, REQUEST *request)
 
                fprintf(fp, "%s\n", what);
 
-               if (debug_flag) {
+               if (rad_debug_lvl) {
                        RDEBUG("Injected %s packet to host %s port 0 code=%d, id=%d", what,
                               inet_ntop(request->reply->src_ipaddr.af,
                                         &request->reply->src_ipaddr.ipaddr,
@@ -1689,7 +1717,7 @@ static int command_inject_to(rad_listen_t *listener, int argc, char *argv[])
        sock->dst_ipaddr = data->my_ipaddr;
        sock->dst_port = data->my_port;
 
-       return 1;
+       return CMD_OK;
 }
 
 static int command_inject_from(rad_listen_t *listener, int argc, char *argv[])
@@ -1722,7 +1750,7 @@ static int command_inject_from(rad_listen_t *listener, int argc, char *argv[])
        }
        sock->inject_client = client;
 
-       return 1;
+       return CMD_OK;
 }
 
 static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
@@ -1766,7 +1794,7 @@ static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
                return 0;
        }
 
-       ret = readvp2(NULL, &vp, fp, &filedone);
+       ret = fr_pair_list_afrom_file(NULL, &vp, fp, &filedone);
        fclose(fp);
        if (ret < 0) {
                cprintf_error(listener, "Failed reading attributes from %s: %s\n",
@@ -1809,7 +1837,7 @@ static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
 #endif
        }
 
-       if (debug_flag) {
+       if (rad_debug_lvl) {
                DEBUG("Injecting %s packet from host %s port 0 code=%d, id=%d",
                                fr_packet_codes[packet->code],
                                inet_ntop(packet->src_ipaddr.af,
@@ -1844,7 +1872,7 @@ static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
 
 #endif
 
-       return 1;
+       return CMD_OK;
 }
 
 
@@ -1983,7 +2011,7 @@ static int command_set_module_config(rad_listen_t *listener, int argc, char *arg
        cs = cf_section_find("modules");
        if (!cs) return 0;
 
-       mi = find_module_instance(cs, argv[0], false);
+       mi = module_find(cs, argv[0]);
        if (!mi) {
                cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
                return 0;
@@ -2050,7 +2078,7 @@ static int command_set_module_config(rad_listen_t *listener, int argc, char *arg
                return 0;
        }
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 static int command_set_module_status(rad_listen_t *listener, int argc, char *argv[])
@@ -2066,7 +2094,7 @@ static int command_set_module_status(rad_listen_t *listener, int argc, char *arg
        cs = cf_section_find("modules");
        if (!cs) return 0;
 
-       mi = find_module_instance(cs, argv[0], false);
+       mi = module_find(cs, argv[0]);
        if (!mi) {
                cprintf_error(listener, "No such module \"%s\"\n", argv[0]);
                return 0;
@@ -2093,7 +2121,7 @@ static int command_set_module_status(rad_listen_t *listener, int argc, char *arg
                mi->force = true;
        }
 
-       return 1;               /* success */
+       return CMD_OK;
 }
 
 #ifdef WITH_STATS
@@ -2150,7 +2178,7 @@ static int command_print_stats(rad_listen_t *listener, fr_stats_t *stats,
                        elapsed_names[i], stats->elapsed[i]);
        }
 
-       return 1;
+       return CMD_OK;
 }
 
 
@@ -2170,10 +2198,42 @@ static int command_stats_queue(rad_listen_t *listener, UNUSED int argc, UNUSED c
        cprintf(listener, "queue_pps_in\t\t" PU "\n", pps[0]);
        cprintf(listener, "queue_pps_out\t\t" PU "\n", pps[1]);
 
-       return 1;
+       return CMD_OK;
 }
 #endif
 
+#ifndef NDEBUG
+static int command_stats_memory(rad_listen_t *listener, int argc, char *argv[])
+{
+
+       if (!main_config.debug_memory || !main_config.memory_report) {
+               cprintf(listener, "No memory debugging was enabled.\n");
+               return CMD_OK;
+       }
+
+       if (argc == 0) goto fail;
+
+       if (strcmp(argv[0], "total") == 0) {
+               cprintf(listener, "%zd\n", talloc_total_size(NULL));
+               return CMD_OK;
+       }
+
+       if (strcmp(argv[0], "blocks") == 0) {
+               cprintf(listener, "%zd\n", talloc_total_blocks(NULL));
+               return CMD_OK;
+       }
+
+       if (strcmp(argv[0], "full") == 0) {
+               cprintf(listener, "see stdout of the server for the full report.\n");
+               fr_log_talloc_report(NULL);
+               return CMD_OK;
+       }
+
+fail:
+       cprintf_error(listener, "Must use 'stats memory [blocks|full|total]'\n");
+       return CMD_FAIL;
+}
+#endif
 
 #ifdef WITH_DETAIL
 static FR_NAME_NUMBER state_names[] = {
@@ -2220,7 +2280,7 @@ static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[])
 
        if ((data->state == STATE_UNOPENED) ||
            (data->state == STATE_UNLOCKED)) {
-               return 1;
+               return CMD_OK;
        }
 
        /*
@@ -2231,7 +2291,7 @@ static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[])
                cprintf(listener, "tries\t0\n");
                cprintf(listener, "offset\t0\n");
                cprintf(listener, "size\t0\n");
-               return 1;
+               return CMD_OK;
        }
 
        cprintf(listener, "packets\t%d\n", data->packets);
@@ -2239,7 +2299,7 @@ static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[])
        cprintf(listener, "offset\t%u\n", (unsigned int) data->offset);
        cprintf(listener, "size\t%u\n", (unsigned int) buf.st_size);
 
-       return 1;
+       return CMD_OK;
 }
 #endif
 
@@ -2270,14 +2330,12 @@ static int command_stats_home_server(rad_listen_t *listener, int argc, char *arg
        }
 
        home = get_home_server(listener, argc, argv, NULL);
-       if (!home) {
-               return 0;
-       }
+       if (!home) return 0;
 
        command_print_stats(listener, &home->stats,
                            (home->type == HOME_TYPE_AUTH), 1);
        cprintf(listener, "outstanding\t%d\n", home->currently_outstanding);
-       return 1;
+       return CMD_OK;
 }
 #endif
 
@@ -2311,9 +2369,7 @@ static int command_stats_client(rad_listen_t *listener, int argc, char *argv[])
                 *      Per-client statistics.
                 */
                client = get_client(listener, argc - 1, argv + 1);
-               if (!client) {
-                       return 0;
-               }
+               if (!client) return 0;
        }
 
        if (strcmp(argv[0], "auth") == 0) {
@@ -2375,9 +2431,7 @@ static int command_stats_socket(rad_listen_t *listener, int argc, char *argv[])
        rad_listen_t *sock;
 
        sock = get_socket(listener, argc, argv, NULL);
-       if (!sock) {
-               return 0;
-       }
+       if (!sock) return 0;
 
        if (sock->type != RAD_LISTEN_AUTH) auth = false;
 
@@ -2411,7 +2465,7 @@ static int command_add_client_file(rad_listen_t *listener, int argc, char *argv[
                return 0;
        }
 
-       return 1;
+       return CMD_OK;
 }
 
 
@@ -2437,7 +2491,7 @@ static int command_del_client(rad_listen_t *listener, int argc, char *argv[])
         */
        client->lifetime = 1;
 
-       return 1;
+       return CMD_OK;
 }
 
 
@@ -2544,6 +2598,12 @@ static fr_command_table_t command_table_stats[] = {
          "- show statistics for given socket",
          command_stats_socket, NULL },
 
+#ifndef NDEBUG
+       { "memory", FR_READ,
+         "stats memory [blocks|full|total] - show statistics on used memory",
+         command_stats_memory, NULL },
+#endif
+
        { NULL, 0, NULL, NULL, NULL }
 };
 #endif
@@ -2673,6 +2733,8 @@ static int command_socket_parse_unix(CONF_SECTION *cs, rad_listen_t *this)
 
        if (this->fd < 0) {
                ERROR("Failed creating control socket \"%s\": %s", sock->path, fr_strerror());
+               if (sock->copy) talloc_free(sock->copy);
+               sock->copy = NULL;
                return -1;
        }
 
@@ -2789,10 +2851,19 @@ static void print_help(rad_listen_t *listener, int argc, char *argv[],
 {
        int i;
 
+       /* this should never happen, but if it does then just return gracefully */
+       if (!table) return;
+
        for (i = 0; table[i].command != NULL; i++) {
                if (argc > 0) {
                        if (strcmp(table[i].command, argv[0]) == 0) {
-                               print_help(listener, argc - 1, argv + 1, table[i].table, recursive);
+                               if (table[i].table) {
+                                       print_help(listener, argc - 1, argv + 1, table[i].table, recursive);
+                               } else {
+                                       if (table[i].help) {
+                                               cprintf(listener, "%s\n", table[i].help);
+                                       }
+                               }
                                return;
                        }
 
@@ -2913,9 +2984,8 @@ static int command_domain_recv_co(rad_listen_t *listener, fr_cs_buffer_t *co)
                                goto do_next;
                        }
 
-                       len = 1;
                        status = table[i].func(listener, argc - 1, argv + 1);
-                       break;
+                       goto do_next;
                }
        }
 
index b0efebf..855c0bb 100644 (file)
@@ -42,6 +42,8 @@ RCSID("$Id$")
 
 #include <ctype.h>
 
+bool check_config = false;
+
 typedef enum conf_property {
        CONF_PROPERTY_INVALID = 0,
        CONF_PROPERTY_NAME,
@@ -74,30 +76,33 @@ struct conf_item {
  *
  */
 struct conf_pair {
-       CONF_ITEM item;
-       char const *attr;               //!< Attribute name
-       char const *value;              //!< Attribute value
-       FR_TOKEN op;                    //!< Operator e.g. =, :=
-       FR_TOKEN lhs_type;              //!< Name quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
-       FR_TOKEN rhs_type;              //!< Value Quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
+       CONF_ITEM       item;
+       char const      *attr;          //!< Attribute name
+       char const      *value;         //!< Attribute value
+       FR_TOKEN        op;             //!< Operator e.g. =, :=
+       FR_TOKEN        lhs_type;       //!< Name quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
+       FR_TOKEN        rhs_type;       //!< Value Quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
+       bool            pass2;          //!< do expansion in pass2.
+       bool            parsed;         //!< Was this item used during parsing?
 };
 
 /** Internal data that is associated with a configuration section
  *
  */
 struct conf_data {
-       CONF_ITEM  item;
-       char const *name;
-       int        flag;
-       void       *data;               //!< User data
-       void       (*free)(void *);     //!< Free user data function
+       CONF_ITEM       item;
+       char const      *name;
+       int             flag;
+       void            *data;          //!< User data
+       void            (*free)(void *);        //!< Free user data function
 };
 
 struct conf_part {
-       CONF_ITEM item;
-       char const      *name1;
-       char const      *name2;
-       FR_TOKEN        name2_type;
+       CONF_ITEM       item;
+       char const      *name1;         //!< First name token.  Given ``foo bar {}`` would be ``foo``.
+       char const      *name2;         //!< Second name token. Given ``foo bar {}`` would be ``bar``.
+
+       FR_TOKEN        name2_type;     //!< The type of quoting around name2.
 
        CONF_ITEM       *children;
        CONF_ITEM       *tail;          //!< For speed.
@@ -114,6 +119,13 @@ struct conf_part {
        CONF_PARSER const *variables;
 };
 
+typedef struct cf_file_t {
+       char const      *filename;
+       CONF_SECTION    *cs;
+       bool            input;
+       struct stat     buf;
+} cf_file_t;
+
 CONF_SECTION *root_config = NULL;
 bool cf_new_escape = false;
 
@@ -126,7 +138,11 @@ static void                *cf_data_find_internal(CONF_SECTION const *cs, char const *name, in
 static char const      *cf_expand_variables(char const *cf, int *lineno,
                                             CONF_SECTION *outercs,
                                             char *output, size_t outsize,
-                                            char const *input);
+                                            char const *input, bool *soft_fail);
+
+static int cf_file_include(CONF_SECTION *cs, char const *filename_in);
+
+
 
 /*
  *     Isolate the scary casts in these tiny provably-safe functions
@@ -268,6 +284,199 @@ static int data_cmp(void const *a, void const *b)
        return strcmp(one->name, two->name);
 }
 
+/*
+ *     Functions for tracking filenames.
+ */
+static int filename_cmp(void const *a, void const *b)
+{
+       cf_file_t const *one = a;
+       cf_file_t const *two = b;
+
+       if (one->buf.st_dev < two->buf.st_dev) return -1;
+       if (one->buf.st_dev > two->buf.st_dev) return +1;
+
+       if (one->buf.st_ino < two->buf.st_ino) return -1;
+       if (one->buf.st_ino > two->buf.st_ino) return +1;
+
+       return 0;
+}
+
+static FILE *cf_file_open(CONF_SECTION *cs, char const *filename)
+{
+       cf_file_t *file;
+       CONF_DATA *cd;
+       CONF_SECTION *top;
+       rbtree_t *tree;
+       int fd;
+       FILE *fp;
+
+       top = cf_top_section(cs);
+       cd = cf_data_find_internal(top, "filename", 0);
+       if (!cd) return NULL;
+
+       tree = cd->data;
+
+       fp = fopen(filename, "r");
+       if (!fp) {
+               ERROR("Unable to open file \"%s\": %s",
+                     filename, fr_syserror(errno));
+               return NULL;
+       }
+
+       fd = fileno(fp);
+
+       file = talloc(tree, cf_file_t);
+       if (!file) {
+               fclose(fp);
+               return NULL;
+       }
+
+       file->filename = filename;
+       file->cs = cs;
+       file->input = true;
+
+       if (fstat(fd, &file->buf) == 0) {
+#ifdef S_IWOTH
+               if ((file->buf.st_mode & S_IWOTH) != 0) {
+                       ERROR("Configuration file %s is globally writable.  "
+                             "Refusing to start due to insecure configuration.", filename);
+
+                       fclose(fp);
+                       talloc_free(file);
+                       return NULL;
+               }
+#endif
+       }
+
+       /*
+        *      We can include the same file twice.  e.g. when it
+        *      contains common definitions, such as for SQL.
+        *
+        *      Though the admin should really use templates for that.
+        */
+       if (!rbtree_insert(tree, file)) {
+               talloc_free(file);
+       }
+
+       return fp;
+}
+
+/*
+ *     Do some checks on the file as an "input" file.  i.e. one read
+ *     by a module.
+ */
+static bool cf_file_input(CONF_SECTION *cs, char const *filename)
+{
+       cf_file_t *file;
+       CONF_DATA *cd;
+       CONF_SECTION *top;
+       rbtree_t *tree;
+
+       top = cf_top_section(cs);
+       cd = cf_data_find_internal(top, "filename", 0);
+       if (!cd) return false;
+
+       tree = cd->data;
+
+       file = talloc(tree, cf_file_t);
+       if (!file) return false;
+
+       file->filename = filename;
+       file->cs = cs;
+       file->input = true;
+
+       if (stat(filename, &file->buf) < 0) {
+               ERROR("Unable to open file \"%s\": %s", filename, fr_syserror(errno));
+               talloc_free(file);
+               return false;
+       }
+
+#ifdef S_IWOTH
+       if ((file->buf.st_mode & S_IWOTH) != 0) {
+               ERROR("Configuration file %s is globally writable.  "
+                     "Refusing to start due to insecure configuration.", filename);
+               talloc_free(file);
+               return false;
+       }
+#endif
+
+       /*
+        *      It's OK to include the same file twice...
+        */
+       if (!rbtree_insert(tree, file)) {
+               talloc_free(file);
+       }
+
+       return true;
+
+}
+
+
+typedef struct cf_file_callback_t {
+       int             rcode;
+       rb_walker_t     callback;
+       CONF_SECTION    *modules;
+} cf_file_callback_t;
+
+
+/*
+ *     Return 0 for keep going, 1 for stop.
+ */
+static int file_callback(void *ctx, void *data)
+{
+       cf_file_callback_t *cb = ctx;
+       cf_file_t *file = data;
+       struct stat buf;
+
+       /*
+        *      The file doesn't exist or we can no longer read it.
+        */
+       if (stat(file->filename, &buf) < 0) {
+               cb->rcode = CF_FILE_ERROR;
+               return 1;
+       }
+
+       /*
+        *      The file changed, we'll need to re-read it.
+        */
+       if (buf.st_mtime != file->buf.st_mtime) {
+               if (!file->input) {
+                       cb->rcode |= CF_FILE_CONFIG;
+               } else {
+                       (void) cb->callback(cb->modules, file->cs);
+                       cb->rcode |= CF_FILE_MODULE;
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+ *     See if any of the files have changed.
+ */
+int cf_file_changed(CONF_SECTION *cs, rb_walker_t callback)
+{
+       CONF_DATA *cd;
+       CONF_SECTION *top;
+       cf_file_callback_t cb;
+       rbtree_t *tree;
+
+       top = cf_top_section(cs);
+       cd = cf_data_find_internal(top, "filename", 0);
+       if (!cd) return true;
+
+       tree = cd->data;
+
+       cb.rcode = CF_FILE_NONE;
+       cb.callback = callback;
+       cb.modules = cf_section_sub_find(cs, "modules");
+
+       (void) rbtree_walk(tree, RBTREE_IN_ORDER, file_callback, &cb);
+
+       return cb.rcode;
+}
+
 static int _cf_section_free(CONF_SECTION *cs)
 {
        /*
@@ -351,8 +560,17 @@ CONF_PAIR *cf_pair_dup(CONF_SECTION *parent, CONF_PAIR *cp)
 
        new = cf_pair_alloc(parent, cp->attr, cf_pair_value(cp),
                            cp->op, cp->lhs_type, cp->rhs_type);
-       if (new) {
-               new->item.lineno = cp->item.lineno;
+       if (!new) return NULL;
+
+       new->parsed = cp->parsed;
+       new->item.lineno = cp->item.lineno;
+
+       /*
+        *      Avoid mallocs if possible.
+        */
+       if (!cp->item.filename || (strcmp(parent->item.filename, cp->item.filename) == 0)) {
+               new->item.filename = parent->item.filename;
+       } else {
                new->item.filename = talloc_strdup(new, cp->item.filename);
        }
 
@@ -383,12 +601,12 @@ CONF_SECTION *cf_section_alloc(CONF_SECTION *parent, char const *name1, char con
 
        if (!name1) return NULL;
 
-       if (name2) {
+       if (name2 && parent) {
                if (strchr(name2, '$')) {
                        name2 = cf_expand_variables(parent->item.filename,
-                                               &parent->item.lineno,
-                                               parent,
-                                               buffer, sizeof(buffer), name2);
+                                                   &parent->item.lineno,
+                                                   parent,
+                                                   buffer, sizeof(buffer), name2, NULL);
                        if (!name2) {
                                ERROR("Failed expanding section name");
                                return NULL;
@@ -438,7 +656,7 @@ CONF_SECTION *cf_section_alloc(CONF_SECTION *parent, char const *name1, char con
  * @note recursively duplicates any child sections.
  * @note does not duplicate any data associated with a section, or its child sections.
  *
- * @param parent section.
+ * @param parent section (may be NULL).
  * @param cs to duplicate.
  * @param name1 of new section.
  * @param name2 of new section.
@@ -462,7 +680,12 @@ CONF_SECTION *cf_section_dup(CONF_SECTION *parent, CONF_SECTION const *cs,
        }
 
        new->item.lineno = cs->item.lineno;
-       new->item.filename = talloc_strdup(new, cs->item.filename);
+
+       if (!cs->item.filename || (parent && (strcmp(parent->item.filename, cs->item.filename) == 0))) {
+               new->item.filename = parent->item.filename;
+       } else {
+               new->item.filename = talloc_strdup(new, cs->item.filename);
+       }
 
        for (ci = cs->children; ci; ci = ci->next) {
                switch (ci->type) {
@@ -762,7 +985,10 @@ CONF_ITEM *cf_reference_item(CONF_SECTION const *parentcs,
         *      section.
         */
        cp = cf_pair_find(cs, p);
-       if (cp) return &(cp->item);
+       if (cp) {
+               cp->parsed = true;      /* conf pairs which are referenced count as parsed */
+               return &(cp->item);
+       }
 
        next = cf_section_sub_find(cs, p);
        if (next) return &(next->item);
@@ -776,7 +1002,6 @@ CONF_ITEM *cf_reference_item(CONF_SECTION const *parentcs,
        }
 
 no_such_item:
-       WARN("No such configuration item %s", ptr);
        return NULL;
 }
 
@@ -799,13 +1024,15 @@ CONF_SECTION *cf_top_section(CONF_SECTION *cs)
 static char const *cf_expand_variables(char const *cf, int *lineno,
                                       CONF_SECTION *outercs,
                                       char *output, size_t outsize,
-                                      char const *input)
+                                      char const *input, bool *soft_fail)
 {
        char *p;
        char const *end, *ptr;
        CONF_SECTION const *parentcs;
        char name[8192];
 
+       if (soft_fail) *soft_fail = false;
+
        /*
         *      Find the master parent conf section.
         *      We can't use main_config.config, because we're in the
@@ -850,7 +1077,7 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
                         */
                        if ((size_t) (end - ptr) >= sizeof(name)) {
                                ERROR("%s[%d]: Reference string is too large",
-                                      cf, *lineno);
+                                     cf, *lineno);
                                return NULL;
                        }
 
@@ -864,7 +1091,8 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
 
                        ci = cf_reference_item(parentcs, outercs, name);
                        if (!ci) {
-                               ERROR("%s[%d]: Reference \"%s\" not found", cf, *lineno, input);
+                               if (soft_fail) *soft_fail = true;
+                               ERROR("%s[%d]: Reference \"${%s}\" not found", cf, *lineno, name);
                                return NULL;
                        }
 
@@ -901,6 +1129,21 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
                                 *  Substitute the value of the variable.
                                 */
                                cp = cf_item_to_pair(ci);
+
+                               /*
+                                *      If the thing we reference is
+                                *      marked up as being expanded in
+                                *      pass2, don't expand it now.
+                                *      Let it be expanded in pass2.
+                                */
+                               if (cp->pass2) {
+                                       if (soft_fail) *soft_fail = true;
+
+                                       ERROR("%s[%d]: Reference \"%s\" points to a variable which has not been expanded.",
+                                             cf, *lineno, input);
+                                       return NULL;
+                               }
+
                                if (!cp->value) {
                                        ERROR("%s[%d]: Reference \"%s\" has no value",
                                               cf, *lineno, input);
@@ -1074,23 +1317,80 @@ static inline int fr_item_validate_ipaddr(CONF_SECTION *cs, char const *name, PW
        }
 }
 
-/*
- *     Parses an item (not a CONF_ITEM) into the specified format,
- *     with a default value.
+/** Parses a #CONF_PAIR into a C data type, with a default value.
  *
- *     Returns -1 on error, -2 if deprecated, 0 for correctly parsed,
- *     and 1 if the default value was used.  Note that the default
- *     value will be used ONLY if the CONF_PAIR is NULL.
+ * Takes fields from a #CONF_PARSER struct and uses them to parse the string value
+ * of a #CONF_PAIR into a C data type matching the type argument.
+ *
+ * The format of the types are the same as #value_data_t types.
+ *
+ * @note The dflt value will only be used if no matching #CONF_PAIR is found. Empty strings will not
+ *      result in the dflt value being used.
+ *
+ * **PW_TYPE to data type mappings**
+ * | PW_TYPE                 | Data type          | Dynamically allocated  |
+ * | ----------------------- | ------------------ | ---------------------- |
+ * | PW_TYPE_TMPL            | ``vp_tmpl_t``      | Yes                    |
+ * | PW_TYPE_BOOLEAN         | ``bool``           | No                     |
+ * | PW_TYPE_INTEGER         | ``uint32_t``       | No                     |
+ * | PW_TYPE_SHORT           | ``uint16_t``       | No                     |
+ * | PW_TYPE_INTEGER64       | ``uint64_t``       | No                     |
+ * | PW_TYPE_SIGNED          | ``int32_t``        | No                     |
+ * | PW_TYPE_STRING          | ``char const *``   | Yes                    |
+ * | PW_TYPE_IPV4_ADDR       | ``fr_ipaddr_t``    | No                     |
+ * | PW_TYPE_IPV4_PREFIX     | ``fr_ipaddr_t``    | No                     |
+ * | PW_TYPE_IPV6_ADDR       | ``fr_ipaddr_t``    | No                     |
+ * | PW_TYPE_IPV6_PREFIX     | ``fr_ipaddr_t``    | No                     |
+ * | PW_TYPE_COMBO_IP_ADDR   | ``fr_ipaddr_t``    | No                     |
+ * | PW_TYPE_COMBO_IP_PREFIX | ``fr_ipaddr_t``    | No                     |
+ * | PW_TYPE_TIMEVAL         | ``struct timeval`` | No                     |
+ *
+ * @param cs to search for matching #CONF_PAIR in.
+ * @param name of #CONF_PAIR to search for.
+ * @param type Data type to parse #CONF_PAIR value as.
+ *     Should be one of the following ``data`` types, and one or more of the following ``flag`` types or'd together:
+ *     - ``data`` #PW_TYPE_TMPL                - @copybrief PW_TYPE_TMPL
+ *                                               Feeds the value into #tmpl_afrom_str. Value can be
+ *                                               obtained when processing requests, with #tmpl_expand or #tmpl_aexpand.
+ *     - ``data`` #PW_TYPE_BOOLEAN             - @copybrief PW_TYPE_BOOLEAN
+ *     - ``data`` #PW_TYPE_INTEGER             - @copybrief PW_TYPE_INTEGER
+ *     - ``data`` #PW_TYPE_SHORT               - @copybrief PW_TYPE_SHORT
+ *     - ``data`` #PW_TYPE_INTEGER64           - @copybrief PW_TYPE_INTEGER64
+ *     - ``data`` #PW_TYPE_SIGNED              - @copybrief PW_TYPE_SIGNED
+ *     - ``data`` #PW_TYPE_STRING              - @copybrief PW_TYPE_STRING
+ *     - ``data`` #PW_TYPE_IPV4_ADDR           - @copybrief PW_TYPE_IPV4_ADDR (IPv4 address with prefix 32).
+ *     - ``data`` #PW_TYPE_IPV4_PREFIX         - @copybrief PW_TYPE_IPV4_PREFIX (IPv4 address with variable prefix).
+ *     - ``data`` #PW_TYPE_IPV6_ADDR           - @copybrief PW_TYPE_IPV6_ADDR (IPv6 address with prefix 128).
+ *     - ``data`` #PW_TYPE_IPV6_PREFIX         - @copybrief PW_TYPE_IPV6_PREFIX (IPv6 address with variable prefix).
+ *     - ``data`` #PW_TYPE_COMBO_IP_ADDR       - @copybrief PW_TYPE_COMBO_IP_ADDR (IPv4/IPv6 address with
+ *                                               prefix 32/128).
+ *     - ``data`` #PW_TYPE_COMBO_IP_PREFIX     - @copybrief PW_TYPE_COMBO_IP_PREFIX (IPv4/IPv6 address with
+ *                                               variable prefix).
+ *     - ``data`` #PW_TYPE_TIMEVAL             - @copybrief PW_TYPE_TIMEVAL
+ *     - ``flag`` #PW_TYPE_DEPRECATED          - @copybrief PW_TYPE_DEPRECATED
+ *     - ``flag`` #PW_TYPE_REQUIRED            - @copybrief PW_TYPE_REQUIRED
+ *     - ``flag`` #PW_TYPE_ATTRIBUTE           - @copybrief PW_TYPE_ATTRIBUTE
+ *     - ``flag`` #PW_TYPE_SECRET              - @copybrief PW_TYPE_SECRET
+ *     - ``flag`` #PW_TYPE_FILE_INPUT          - @copybrief PW_TYPE_FILE_INPUT
+ *     - ``flag`` #PW_TYPE_NOT_EMPTY           - @copybrief PW_TYPE_NOT_EMPTY
+ * @param data Pointer to a global variable, or pointer to a field in the struct being populated with values.
+ * @param dflt value to use, if no #CONF_PAIR is found.
+ * @return
+ *     - 1 if default value was used.
+ *     - 0 on success.
+ *     - -1 on error.
+ *     - -2 if deprecated.
  */
 int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *data, char const *dflt)
 {
        int rcode;
-       bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, xlat;
+       bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, multi;
        char **q;
        char const *value;
-       CONF_PAIR const *cp = NULL;
+       CONF_PAIR *cp = NULL;
        fr_ipaddr_t *ipaddr;
        char buffer[8192];
+       CONF_ITEM *c_item = &cs->item;
 
        if (!cs) return -1;
 
@@ -1101,30 +1401,63 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
        file_input = (type == PW_TYPE_FILE_INPUT);      /* check, not and */
        cant_be_empty = (type & PW_TYPE_NOT_EMPTY);
        tmpl = (type & PW_TYPE_TMPL);
-       xlat = (type & PW_TYPE_XLAT);
+       multi = (type & PW_TYPE_MULTI);
 
        if (attribute) required = true;
        if (required) cant_be_empty = true;     /* May want to review this in the future... */
 
+       /*
+        *      Everything except templates must have a base type.
+        */
+       if (!(type & 0xff) && !tmpl) {
+               cf_log_err(c_item, "Configuration item \"%s\" must have a data type", name);
+               return -1;
+       }
+
        type &= 0xff;                           /* normal types are small */
+
        rcode = 0;
 
        cp = cf_pair_find(cs, name);
-       if (cp) {
-               value = cp->value;
-       } else {
+
+       /*
+        *      No pairs match the configuration item name in the current
+        *      section, use the default value.
+        */
+       if (!cp) {
+               if (deprecated) return 0;       /* Don't set the default value */
+
                rcode = 1;
                value = dflt;
+       /*
+        *      Something matched, used the CONF_PAIR value.
+        */
+       } else {
+               CONF_PAIR *next = cp;
+               value = cp->value;
+               cp->parsed = true;
+               c_item = &cp->item;
+
+               /*
+                *      @fixme We should actually validate
+                *      the value of the pairs too
+                */
+               if (multi) while ((next = cf_pair_find_next(cs, next, name))) {
+                       next->parsed = true;
+               }
+
+               if (deprecated) {
+                       cf_log_err(c_item, "Configuration item \"%s\" is deprecated", name);
+
+                       return -2;
+               }
        }
 
        if (!value) {
                if (required) {
                is_required:
-                       if (!cp) {
-                               cf_log_err(&(cs->item), "Configuration item '%s' must have a value", name);
-                       } else {
-                               cf_log_err(&(cp->item), "Configuration item '%s' must have a value", name);
-                       }
+                       cf_log_err(c_item, "Configuration item \"%s\" must have a value", name);
+
                        return -1;
                }
                return rcode;
@@ -1132,107 +1465,29 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
 
        if ((value[0] == '\0') && cant_be_empty) {
        cant_be_empty:
-               if (!cp) {
-                       cf_log_err(&(cs->item), "Configuration item '%s' must not be empty (zero length)", name);
-                       if (!required) cf_log_err(&(cs->item), "Comment item to silence this message");
-               } else {
-                       cf_log_err(&(cp->item), "Configuration item '%s' must not be empty (zero length)", name);
-                       if (!required) cf_log_err(&(cp->item), "Comment item to silence this message");
-               }
+               cf_log_err(c_item, "Configuration item \"%s\" must not be empty (zero length)", name);
+               if (!required) cf_log_err(c_item, "Comment item to silence this message");
+
                return -1;
        }
 
-       if (deprecated) {
-               cf_log_err(&(cs->item), "Configuration item \"%s\" is deprecated", name);
-
-               return -2;
-       }
 
        /*
-        *
+        *      Process a value as a LITERAL template.  Once all of
+        *      the attrs and xlats are defined, the pass2 code
+        *      converts it to the appropriate type.
         */
        if (tmpl) {
-               ssize_t slen;
-               value_pair_tmpl_t *vpt;
+               vp_tmpl_t *vpt;
 
-               if (!value || (cf_pair_value_type(cp) == T_INVALID)) {
-                       *(value_pair_tmpl_t **)data = NULL;
+               if (!value) {
+                       *(vp_tmpl_t **)data = NULL;
                        return 0;
                }
 
-               slen = tmpl_afrom_str(cs, &vpt, value, strlen(value),
-                                     cf_pair_value_type(cp), REQUEST_CURRENT, PAIR_LIST_REQUEST);
-               if (slen < 0) {
-                       char *spaces, *text;
-
-                       fr_canonicalize_error(cs, &spaces, &text, slen, fr_strerror());
-
-                       cf_log_err_cs(cs, "Failed parsing configuration item '%s'", name);
-                       cf_log_err_cs(cs, "%s", value);
-                       cf_log_err_cs(cs, "%s^ %s", spaces, text);
-
-                       talloc_free(spaces);
-                       talloc_free(text);
-
-                       return -1;
-               }
-
-               /*
-                *      Sanity check
-                *
-                *      Don't add default - update with new types.
-                */
-               switch (vpt->type) {
-               case TMPL_TYPE_LITERAL:
-               case TMPL_TYPE_ATTR:
-               case TMPL_TYPE_ATTR_UNDEFINED:
-               case TMPL_TYPE_LIST:
-               case TMPL_TYPE_DATA:
-               case TMPL_TYPE_EXEC:
-               case TMPL_TYPE_XLAT:
-               case TMPL_TYPE_XLAT_STRUCT:
-                       break;
-
-               case TMPL_TYPE_UNKNOWN:
-               case TMPL_TYPE_REGEX:
-               case TMPL_TYPE_REGEX_STRUCT:
-               case TMPL_TYPE_NULL:
-                       rad_assert(0);
-               }
-
-               /*
-                *      If the attribute flag is set, the template must be an
-                *      attribute reference.
-                */
-               if (attribute && (vpt->type != TMPL_TYPE_ATTR)) {
-                       cf_log_err(&(cs->item), "Configuration item '%s' must be an attr "
-                                  "but is an %s", name, fr_int2str(tmpl_names, vpt->type, "<INVALID>"));
-                       talloc_free(vpt);
-                       return -1;
-               }
-
-               /*
-                *      If the xlat flag is set, the template must be an xlat
-                */
-               if (xlat && (vpt->type != TMPL_TYPE_XLAT_STRUCT)) {
-                       cf_log_err(&(cs->item), "Configuration item '%s' must be an xlat expansion but is an %s",
-                                  name, fr_int2str(tmpl_names, vpt->type, "<INVALID>"));
-                       talloc_free(vpt);
-                       return -1;
-               }
-
-               /*
-                *      If we have a type, and the template is an attribute reference
-                *      check that the attribute reference matches the type.
-                */
-               if ((type > 0) && (vpt->type == TMPL_TYPE_ATTR) && (vpt->tmpl_da->type != type)) {
-                       cf_log_err(&(cs->item), "Configuration item '%s' attr must be an %s, but is an %s",
-                                  name, fr_int2str(dict_attr_types, type, "<INVALID>"),
-                                  fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"));
-                       talloc_free(vpt);
-                       return -1;
-               }
-               *(value_pair_tmpl_t **)data = vpt;
+               rad_assert(!attribute);
+               vpt = tmpl_alloc(cs, TMPL_TYPE_LITERAL, value, strlen(value));
+               *(vp_tmpl_t **)data = vpt;
 
                return 0;
        }
@@ -1240,12 +1495,14 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
        switch (type) {
        case PW_TYPE_BOOLEAN:
                /*
-                *      Allow yes/no and on/off
+                *      Allow yes/no, true/false, and on/off
                 */
                if ((strcasecmp(value, "yes") == 0) ||
+                   (strcasecmp(value, "true") == 0) ||
                    (strcasecmp(value, "on") == 0)) {
                        *(bool *)data = true;
                } else if ((strcasecmp(value, "no") == 0) ||
+                          (strcasecmp(value, "false") == 0) ||
                           (strcasecmp(value, "off") == 0)) {
                        *(bool *)data = false;
                } else {
@@ -1280,6 +1537,20 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
        }
                break;
 
+       case PW_TYPE_BYTE:
+       {
+               unsigned long v = strtoul(value, 0, 0);
+
+               if (v > UINT8_MAX) {
+                       cf_log_err(&(cs->item), "Invalid value \"%s\" for variable %s, must be between 0-%u", value,
+                                  name, UINT8_MAX);
+                       return -1;
+               }
+               *(uint8_t *)data = (uint8_t) v;
+               cf_log_info(cs, "%.*s\t%s = %u", cs->depth, parse_spaces, name, *(uint8_t *)data);
+       }
+               break;
+
        case PW_TYPE_SHORT:
        {
                unsigned long v = strtoul(value, 0, 0);
@@ -1323,7 +1594,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
                        value = cf_expand_variables("<internal>",
                                                    &lineno,
                                                    cs, buffer, sizeof(buffer),
-                                                   value);
+                                                   value, NULL);
                        if (!value) {
                                cf_log_err(&(cs->item),"Failed expanding variable %s", name);
                                return -1;
@@ -1348,7 +1619,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
                /*
                 *      Hide secrets when using "radiusd -X".
                 */
-               if (secret && (debug_flag <= 2)) {
+               if (secret && (rad_debug_lvl <= 2)) {
                        cf_log_info(cs, "%.*s\t%s = <<< secret >>>",
                                    cs->depth, parse_spaces, name);
                } else {
@@ -1363,21 +1634,8 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
                 *      to be caught as early as possible, during
                 *      server startup.
                 */
-               if (*q && file_input) {
-                       struct stat buf;
-
-                       if (stat(*q, &buf) < 0) {
-                               char user[255], group[255];
-
-                               ERROR("Unable to open file \"%s\": %s", value, fr_syserror(errno));
-                               ERROR("Our effective user and group was %s:%s",
-                                     (rad_prints_uid(NULL, user, sizeof(user), geteuid()) < 0) ?
-                                     "unknown" : user,
-                                     (rad_prints_gid(NULL, group, sizeof(group), getegid()) < 0) ?
-                                     "unknown" : group );
-
-                               return -1;
-                       }
+               if (*q && file_input && !cf_file_input(cs, *q)) {
+                       return -1;
                }
                break;
 
@@ -1407,7 +1665,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
        case PW_TYPE_COMBO_IP_PREFIX:
                ipaddr = data;
 
-               if (fr_pton(ipaddr, value, -1, true) < 0) {
+               if (fr_pton(ipaddr, value, -1, AF_UNSPEC, true) < 0) {
                        ERROR("%s", fr_strerror());
                        return -1;
                }
@@ -1469,6 +1727,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
 
                cpn = cf_pair_alloc(cs, name, value, T_OP_SET, T_BARE_WORD, T_BARE_WORD);
                if (!cpn) return -1;
+               cpn->parsed = true;
                cpn->item.filename = "<internal>";
                cpn->item.lineno = 0;
                cf_item_add(cs, &(cpn->item));
@@ -1503,8 +1762,7 @@ static void cf_section_parse_init(CONF_SECTION *cs, void *base,
                         *      etc. allocated in the subsection.
                         */
                        if (!subcs) {
-                               subcs = cf_section_alloc(cs, variables[i].name,
-                                                        NULL);
+                               subcs = cf_section_alloc(cs, variables[i].name, NULL);
                                if (!subcs) return;
 
                                subcs->item.filename = cs->item.filename;
@@ -1512,7 +1770,7 @@ static void cf_section_parse_init(CONF_SECTION *cs, void *base,
                                cf_item_add(cs, &(subcs->item));
                        }
 
-                       cf_section_parse_init(subcs, base,
+                       cf_section_parse_init(subcs, (uint8_t *)base + variables[i].offset,
                                              (CONF_PARSER const *) variables[i].dflt);
                        continue;
                }
@@ -1534,24 +1792,58 @@ static void cf_section_parse_init(CONF_SECTION *cs, void *base,
 }
 
 
-/*
- *     Parse a configuration section into user-supplied variables.
+static void cf_section_parse_warn(CONF_SECTION *cs)
+{
+       CONF_ITEM *ci;
+
+       for (ci = cs->children; ci; ci = ci->next) {
+               /*
+                *      Don't recurse on sections. We can only safely
+                *      check conf pairs at the same level as the
+                *      section that was just parsed.
+                */
+               if (ci->type == CONF_ITEM_SECTION) continue;
+               if (ci->type == CONF_ITEM_PAIR) {
+                       CONF_PAIR *cp;
+
+                       cp = cf_item_to_pair(ci);
+                       if (cp->parsed) continue;
+
+                       WARN("%s[%d]: The item '%s' is defined, but is unused by the configuration",
+                            cp->item.filename ? cp->item.filename : "unknown",
+                            cp->item.lineno ? cp->item.lineno : 0,
+                               cp->attr);
+               }
+
+               /*
+                *      Skip everything else.
+                */
+       }
+}
+
+/** Parse a configuration section into user-supplied variables
+ *
+ * @param cs to parse.
+ * @param base pointer to a struct to fill with data.  Any buffers will also be talloced
+ *     using this parent as a pointer.
+ * @param variables mappings between struct fields and #CONF_ITEM s.
+ * @return
+ *     - 0 on success.
+ *     - -1 on general error.
+ *     - -2 if a deprecated #CONF_ITEM was found.
  */
-int cf_section_parse(CONF_SECTION *cs, void *base,
-                    CONF_PARSER const *variables)
+int cf_section_parse(CONF_SECTION *cs, void *base, CONF_PARSER const *variables)
 {
-       int ret;
+       int ret = 0;
        int i;
        void *data;
 
        cs->variables = variables; /* this doesn't hurt anything */
 
        if (!cs->name2) {
-               cf_log_info(cs, "%.*s%s {", cs->depth, parse_spaces,
-                      cs->name1);
+               cf_log_info(cs, "%.*s%s {", cs->depth, parse_spaces, cs->name1);
        } else {
-               cf_log_info(cs, "%.*s%s %s {", cs->depth, parse_spaces,
-                      cs->name1, cs->name2);
+               cf_log_info(cs, "%.*s%s %s {", cs->depth, parse_spaces, cs->name1, cs->name2);
        }
 
        cf_section_parse_init(cs, base, variables);
@@ -1565,18 +1857,21 @@ int cf_section_parse(CONF_SECTION *cs, void *base,
                 */
                if (variables[i].type == PW_TYPE_SUBSECTION) {
                        CONF_SECTION *subcs;
-                       subcs = cf_section_sub_find(cs, variables[i].name);
 
+                       subcs = cf_section_sub_find(cs, variables[i].name);
+                       /*
+                        *      Default in this case is overloaded to mean a pointer
+                        *      to the CONF_PARSER struct for the subsection.
+                        */
                        if (!variables[i].dflt || !subcs) {
-                               DEBUG2("Internal sanity check 1 failed in cf_section_parse %s",
-                                      variables[i].name);
-                               goto error;
+                               ERROR("Internal sanity check 1 failed in cf_section_parse %s", variables[i].name);
+                               ret = -1;
+                               goto finish;
                        }
 
-                       if (cf_section_parse(subcs, base,
-                                            (CONF_PARSER const *) variables[i].dflt) < 0) {
-                               goto error;
-                       }
+                       ret = cf_section_parse(subcs, (uint8_t *)base + variables[i].offset,
+                                              (CONF_PARSER const *) variables[i].dflt);
+                       if (ret < 0) goto finish;
                        continue;
                } /* else it's a CONF_PAIR */
 
@@ -1585,37 +1880,54 @@ int cf_section_parse(CONF_SECTION *cs, void *base,
                } else if (base) {
                        data = ((char *)base) + variables[i].offset;
                } else {
-                       DEBUG2("Internal sanity check 2 failed in cf_section_parse");
-                       goto error;
+                       ERROR("Internal sanity check 2 failed in cf_section_parse");
+                       ret = -1;
+                       goto finish;
                }
 
                /*
                 *      Parse the pair we found, or a default value.
                 */
                ret = cf_item_parse(cs, variables[i].name, variables[i].type, data, variables[i].dflt);
-               if (ret < 0) {
-                       /*
-                        *      Be nice, and print the name of the new config item.
-                        */
-                       if ((ret == -2) && (variables[i + 1].offset == variables[i].offset) &&
+               switch (ret) {
+               case 1:         /* Used default */
+                       ret = 0;
+                       break;
+
+               case 0:         /* OK */
+                       break;
+
+               case -1:        /* Parse error */
+                       goto finish;
+
+               case -2:        /* Deprecated CONF ITEM */
+                       if ((variables[i + 1].offset == variables[i].offset) &&
                            (variables[i + 1].data == variables[i].data)) {
                                cf_log_err(&(cs->item), "Replace \"%s\" with \"%s\"", variables[i].name,
                                           variables[i + 1].name);
                        }
-
-                       goto error;
+                       goto finish;
                }
        } /* for all variables in the configuration section */
 
-       cf_log_info(cs, "%.*s}", cs->depth, parse_spaces);
+       /*
+        *      Ensure we have a proper terminator, type so we catch
+        *      missing terminators reliably
+        */
+       rad_assert(variables[i].type == -1);
 
-       cs->base = base;
+       /*
+        *      Warn about items in the configuration which weren't
+        *      checked during parsing.
+        */
+       if (rad_debug_lvl >= 3) cf_section_parse_warn(cs);
 
-       return 0;
+       cs->base = base;
 
- error:
        cf_log_info(cs, "%.*s}", cs->depth, parse_spaces);
-       return -1;
+
+finish:
+       return ret;
 }
 
 
@@ -1635,6 +1947,7 @@ int cf_section_parse_pass2(CONF_SECTION *cs, void *base, CONF_PARSER const *vari
         */
        for (i = 0; variables[i].name != NULL; i++) {
                CONF_PAIR *cp;
+               void *data;
 
                /*
                 *      Handle subsections specially
@@ -1643,17 +1956,29 @@ int cf_section_parse_pass2(CONF_SECTION *cs, void *base, CONF_PARSER const *vari
                        CONF_SECTION *subcs;
                        subcs = cf_section_sub_find(cs, variables[i].name);
 
-                       if (cf_section_parse_pass2(subcs, base,
+                       if (cf_section_parse_pass2(subcs, (uint8_t *)base + variables[i].offset,
                                                   (CONF_PARSER const *) variables[i].dflt) < 0) {
                                return -1;
                        }
                        continue;
                } /* else it's a CONF_PAIR */
 
+               /*
+                *      Figure out which data we need to fix.
+                */
+               if (variables[i].data) {
+                       data = variables[i].data; /* prefer this. */
+               } else if (base) {
+                       data = ((char *)base) + variables[i].offset;
+               } else {
+                       data = NULL;
+               }
+
                cp = cf_pair_find(cs, variables[i].name);
+               xlat = NULL;
 
        redo:
-               if (!cp || !cp->value) continue;
+               if (!cp || !cp->value || !data) continue;
 
                if ((cp->rhs_type != T_DOUBLE_QUOTED_STRING) &&
                    (cp->rhs_type != T_BARE_WORD)) continue;
@@ -1678,30 +2003,87 @@ int cf_section_parse_pass2(CONF_SECTION *cs, void *base, CONF_PARSER const *vari
                }
 
                /*
-                *      xlat expansions should be parseable.
+                *      Parse (and throw away) the xlat string.
+                *
+                *      FIXME: All of these should be converted from PW_TYPE_XLAT
+                *      to PW_TYPE_TMPL.
                 */
-               value = talloc_strdup(cs, cp->value); /* modified by xlat_tokenize */
-               xlat = NULL;
+               if ((variables[i].type & PW_TYPE_XLAT) != 0) {
+                       /*
+                        *      xlat expansions should be parseable.
+                        */
+                       value = talloc_strdup(cs, cp->value); /* modified by xlat_tokenize */
+                       xlat = NULL;
+
+                       slen = xlat_tokenize(cs, value, &xlat, &error);
+                       if (slen < 0) {
+                               char *spaces, *text;
 
-               slen = xlat_tokenize(cs, value, &xlat, &error);
-               if (slen < 0) {
-                       char *spaces, *text;
+                       error:
+                               fr_canonicalize_error(cs, &spaces, &text, slen, cp->value);
 
-                       fr_canonicalize_error(cs, &spaces, &text, slen, cp->value);
+                               cf_log_err(&cp->item, "Failed parsing expanded string:");
+                               cf_log_err(&cp->item, "%s", text);
+                               cf_log_err(&cp->item, "%s^ %s", spaces, error);
 
-                       cf_log_err(&cp->item, "Failed parsing expanded string:");
-                       cf_log_err(&cp->item, "%s", text);
-                       cf_log_err(&cp->item, "%s^ %s", spaces, error);
+                               talloc_free(spaces);
+                               talloc_free(text);
+                               talloc_free(value);
+                               talloc_free(xlat);
+                               return -1;
+                       }
 
-                       talloc_free(spaces);
-                       talloc_free(text);
                        talloc_free(value);
                        talloc_free(xlat);
-                       return -1;
                }
 
-               talloc_free(value);
-               talloc_free(xlat);
+               /*
+                *      Convert the LITERAL template to the actual
+                *      type.
+                */
+               if ((variables[i].type & PW_TYPE_TMPL) != 0) {
+                       vp_tmpl_t *vpt;
+
+                       slen = tmpl_afrom_str(cs, &vpt, cp->value, talloc_array_length(cp->value) - 1,
+                                             cp->rhs_type,
+                                             REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
+                       if (slen < 0) {
+                               error = fr_strerror();
+                               goto error;
+                       }
+
+                       /*
+                        *      Sanity check
+                        *
+                        *      Don't add default - update with new types.
+                        */
+                       switch (vpt->type) {
+                       /*
+                        *      All attributes should have been defined by this point.
+                        */
+                       case TMPL_TYPE_ATTR_UNDEFINED:
+                               cf_log_err(&cp->item, "Unknown attribute '%s'", vpt->tmpl_unknown_name);
+                               return -1;
+
+                       case TMPL_TYPE_LITERAL:
+                       case TMPL_TYPE_ATTR:
+                       case TMPL_TYPE_LIST:
+                       case TMPL_TYPE_DATA:
+                       case TMPL_TYPE_EXEC:
+                       case TMPL_TYPE_XLAT:
+                       case TMPL_TYPE_XLAT_STRUCT:
+                               break;
+
+                       case TMPL_TYPE_UNKNOWN:
+                       case TMPL_TYPE_REGEX:
+                       case TMPL_TYPE_REGEX_STRUCT:
+                       case TMPL_TYPE_NULL:
+                               rad_assert(0);
+                       }
+
+                       talloc_free(*(vp_tmpl_t **)data);
+                       *(vp_tmpl_t **)data = vpt;
+               }
 
                /*
                 *      If the "multi" flag is set, check all of them.
@@ -1830,7 +2212,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                           CONF_SECTION *current)
 
 {
-       CONF_SECTION *this, *css, *nextcs;
+       CONF_SECTION *this, *css;
        CONF_PAIR *cpn;
        char const *ptr;
        char const *value;
@@ -1841,9 +2223,9 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
        char buf4[8192];
        FR_TOKEN t1 = T_INVALID, t2, t3;
        bool has_spaces = false;
+       bool pass2;
        char *cbuf = buf;
        size_t len;
-       fr_cond_t *cond = NULL;
 
        this = current;         /* add items here */
 
@@ -1852,7 +2234,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
         */
        for (;;) {
                int at_eof;
-               nextcs = NULL;
+               css = NULL;
 
                /*
                 *      Get data, and remember if we are at EOF.
@@ -1928,6 +2310,8 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                has_spaces = false;
 
        get_more:
+               pass2 = false;
+
                /*
                 *      The parser is getting to be evil.
                 */
@@ -1992,6 +2376,8 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                       goto check_for_more;
               }
 
+              if (t1 != T_BARE_WORD) goto skip_keywords;
+
                /*
                 *      Allow for $INCLUDE files
                 *
@@ -2011,7 +2397,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
 
                        if (buf2[0] == '$') relative = false;
 
-                       value = cf_expand_variables(filename, lineno, this, buf4, sizeof(buf4), buf2);
+                       value = cf_expand_variables(filename, lineno, this, buf4, sizeof(buf4), buf2, NULL);
                        if (!value) return -1;
 
                        if (!FR_DIR_IS_RELATIVE(value)) relative = false;
@@ -2174,6 +2560,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                        char const *error = NULL;
                        char *p;
                        CONF_SECTION *server;
+                       fr_cond_t *cond = NULL;
 
                        /*
                         *      if / elsif MUST be inside of a
@@ -2188,9 +2575,21 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                        }
 
                        /*
+                        *      Can only have "if" in 3 named sections.
+                        */
+                       server = this->item.parent;
+                       while (server &&
+                              (strcmp(server->name1, "server") != 0) &&
+                              (strcmp(server->name1, "policy") != 0) &&
+                              (strcmp(server->name1, "instantiate") != 0)) {
+                               server = server->item.parent;
+                               if (!server) goto invalid_location;
+                       }
+
+                       /*
                         *      Skip (...) to find the {
                         */
-                       slen = fr_condition_tokenize(nextcs, cf_section_to_item(nextcs), ptr, &cond,
+                       slen = fr_condition_tokenize(this, cf_section_to_item(this), ptr, &cond,
                                                     &error, FR_COND_TWO_PASS);
                        memcpy(&p, &ptr, sizeof(p));
 
@@ -2224,7 +2623,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                ptr = cf_expand_variables(filename, lineno,
                                                          this,
                                                          buf3, sizeof(buf3),
-                                                         ptr);
+                                                         ptr, NULL);
                                if (!ptr) {
                                        ERROR("%s[%d]: Parse error expanding ${...} in condition",
                                              filename, *lineno);
@@ -2232,24 +2631,16 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                }
                        } /* else leave it alone */
 
-                       server = this->item.parent;
-                       while ((strcmp(server->name1, "server") != 0) &&
-                              (strcmp(server->name1, "policy") != 0) &&
-                              (strcmp(server->name1, "instantiate") != 0)) {
-                               server = server->item.parent;
-                               if (!server) goto invalid_location;
-                       }
-
-                       nextcs = cf_section_alloc(this, buf1, ptr);
-                       if (!nextcs) {
+                       css = cf_section_alloc(this, buf1, ptr);
+                       if (!css) {
                                ERROR("%s[%d]: Failed allocating memory for section",
                                      filename, *lineno);
                                return -1;
                        }
-                       nextcs->item.filename = talloc_strdup(nextcs, filename);
-                       nextcs->item.lineno = *lineno;
+                       css->item.filename = filename;
+                       css->item.lineno = *lineno;
 
-                       slen = fr_condition_tokenize(nextcs, cf_section_to_item(nextcs), ptr, &cond,
+                       slen = fr_condition_tokenize(css, cf_section_to_item(css), ptr, &cond,
                                                     &error, FR_COND_TWO_PASS);
                        *p = '{'; /* put it back */
 
@@ -2257,7 +2648,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                        if (slen < 0) {
                                char *spaces, *text;
 
-                               fr_canonicalize_error(nextcs, &spaces, &text, slen, ptr);
+                               fr_canonicalize_error(this, &spaces, &text, slen, ptr);
 
                                ERROR("%s[%d]: Parse error in condition",
                                      filename, *lineno);
@@ -2266,12 +2657,12 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
 
                                talloc_free(spaces);
                                talloc_free(text);
-                               talloc_free(nextcs);
+                               talloc_free(css);
                                return -1;
                        }
 
                        if ((size_t) slen >= (sizeof(buf2) - 1)) {
-                               talloc_free(nextcs);
+                               talloc_free(css);
                                ERROR("%s[%d]: Condition is too large after \"%s\"",
                                       filename, *lineno, buf1);
                                return -1;
@@ -2289,10 +2680,9 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                        memcpy(buf2, ptr, slen);
                        buf2[slen] = '\0';
                        ptr = p;
-                       t2 = T_BARE_WORD;
 
                        if ((t3 = gettoken(&ptr, buf3, sizeof(buf3), true)) != T_LCBRACE) {
-                               talloc_free(nextcs);
+                               talloc_free(css);
                                ERROR("%s[%d]: Expected '{' %d",
                                      filename, *lineno, t3);
                                return -1;
@@ -2302,13 +2692,22 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                         *      Swap the condition with trailing stuff for
                         *      the final condition.
                         */
-                       memcpy(&p, &nextcs->name2, sizeof(nextcs->name2));
+                       memcpy(&p, &css->name2, sizeof(css->name2));
                        talloc_free(p);
-                       nextcs->name2 = talloc_typed_strdup(nextcs, buf2);
+                       css->name2 = talloc_typed_strdup(css, buf2);
+
+                       cf_item_add(this, &(css->item));
+                       cf_data_add_internal(css, "if", cond, NULL, false);
 
-                       goto section_alloc;
+                       /*
+                        *      The current section is now the child section.
+                        */
+                       this = css;
+                       css = NULL;
+                       goto check_for_more;
                }
 
+       skip_keywords:
                /*
                 *      Grab the next token.
                 */
@@ -2378,25 +2777,27 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                        }
 
                        /*
-                        *      These are not allowed.  Print a
-                        *      helpful error message.
-                        */
-                       if ((t3 == T_BACK_QUOTED_STRING) &&
-                           (!this || (strcmp(this->name1, "update") != 0))) {
-                               ERROR("%s[%d]: Syntax error: Invalid string `...` in assignment",
-                                      filename, *lineno);
-                               return -1;
-                       }
-
-                       /*
-                        *      Handle variable substitution via ${foo}
+                        *      Allow "foo" by itself, or "foo = bar"
                         */
                        switch (t3) {
+                               bool soft_fail;
+
                        case T_BARE_WORD:
                        case T_DOUBLE_QUOTED_STRING:
                        case T_BACK_QUOTED_STRING:
-                               value = cf_expand_variables(filename, lineno, this, buf4, sizeof(buf4), buf3);
-                               if (!value) return -1;
+                               value = cf_expand_variables(filename, lineno, this, buf4, sizeof(buf4), buf3, &soft_fail);
+                               if (!value) {
+                                       if (!soft_fail) return -1;
+
+                                       /*
+                                        *      References an item which doesn't exist,
+                                        *      or which is already marked up as being
+                                        *      expanded in pass2.  Wait for pass2 to
+                                        *      do the expansions.
+                                        */
+                                       pass2 = true;
+                                       value = buf3;
+                               }
                                break;
 
                        case T_EOL:
@@ -2415,8 +2816,9 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                do_set:
                        cpn = cf_pair_alloc(this, buf1, value, t2, t1, t3);
                        if (!cpn) return -1;
-                       cpn->item.filename = talloc_strdup(cpn, filename);
+                       cpn->item.filename = filename;
                        cpn->item.lineno = *lineno;
+                       cpn->pass2 = pass2;
                        cf_item_add(this, &(cpn->item));
 
                        /*
@@ -2467,29 +2869,18 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                        /* FALL-THROUGH */
 
                case T_LCBRACE:
-               section_alloc:
-                       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;
-                               }
-
-                               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 */
+                       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;
                        }
 
+                       css->item.filename = filename;
+                       css->item.lineno = *lineno;
+                       cf_item_add(this, &(css->item));
+
                        /*
                         *      There may not be a name2
                         */
@@ -2542,86 +2933,78 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
 /*
  *     Include one config file in another.
  */
-int cf_file_include(CONF_SECTION *cs, char const *filename)
+static int cf_file_include(CONF_SECTION *cs, char const *filename_in)
 {
        FILE            *fp;
        int             lineno = 0;
-       struct stat     statbuf;
-       time_t          *mtime;
-       CONF_DATA       *cd;
+       char const      *filename;
+
+       /*
+        *      So we only need to do this once.
+        */
+       filename = talloc_strdup(cs, filename_in);
 
        DEBUG2("including configuration file %s", filename);
 
-       fp = fopen(filename, "r");
-       if (!fp) {
-               ERROR("Unable to open file \"%s\": %s",
-                      filename, fr_syserror(errno));
+       fp = cf_file_open(cs, filename);
+       if (!fp) return -1;
+
+       if (!cs->item.filename) cs->item.filename = filename;
+
+       /*
+        *      Read the section.  It's OK to have EOF without a
+        *      matching close brace.
+        */
+       if (cf_section_read(filename, &lineno, fp, cs) < 0) {
+               fclose(fp);
                return -1;
        }
 
-       if (stat(filename, &statbuf) == 0) {
-#ifdef S_IWOTH
-               if ((statbuf.st_mode & S_IWOTH) != 0) {
-                       fclose(fp);
-                       ERROR("Configuration file %s is globally writable.  "
-                             "Refusing to start due to insecure configuration.", filename);
-                       return -1;
-               }
-#endif
+       fclose(fp);
+       return 0;
+}
 
-#if 0 && defined(S_IROTH)
-               if (statbuf.st_mode & S_IROTH) != 0) {
-                       fclose(fp);
-                       ERROR("Configuration file %s is globally readable.  "
-                             "Refusing to start due to insecure configuration", filename);
-                       return -1;
-               }
-#endif
-       }
 
-       if (cf_data_find_internal(cs, filename, PW_TYPE_FILE_INPUT)) {
-               fclose(fp);
-               ERROR("Cannot include the same file twice: \"%s\"", filename);
+/*
+ *     Do variable expansion in pass2.
+ *
+ *     This is a breadth-first expansion.  "deep
+ */
+static int cf_section_pass2(CONF_SECTION *cs)
+{
+       CONF_ITEM *ci;
 
-               return -1;
-       }
+       for (ci = cs->children; ci; ci = ci->next) {
+               char const *value;
+               CONF_PAIR *cp;
+               char buffer[8192];
 
-       /*
-        *      Add the filename to the section
-        */
-       mtime = talloc(cs, time_t);
-       *mtime = statbuf.st_mtime;
+               if (ci->type != CONF_ITEM_PAIR) continue;
 
-       if (cf_data_add_internal(cs, filename, mtime, NULL, PW_TYPE_FILE_INPUT) < 0) {
-               fclose(fp);
-               ERROR("Internal error opening file \"%s\"",
-                      filename);
-               return -1;
-       }
+               cp = cf_item_to_pair(ci);
+               if (!cp->value || !cp->pass2) continue;
 
-       cd = cf_data_find_internal(cs, filename, PW_TYPE_FILE_INPUT);
-       if (!cd) {
-               fclose(fp);
-               ERROR("Internal error opening file \"%s\"",
-                      filename);
-               return -1;
+               rad_assert((cp->rhs_type == T_BARE_WORD) ||
+                          (cp->rhs_type == T_DOUBLE_QUOTED_STRING) ||
+                          (cp->rhs_type == T_BACK_QUOTED_STRING));
+
+               value = cf_expand_variables(ci->filename, &ci->lineno, cs, buffer, sizeof(buffer), cp->value, NULL);
+               if (!value) return -1;
+
+               rad_const_free(cp->value);
+               cp->value = talloc_typed_strdup(cp, value);
        }
 
-       if (!cs->item.filename) cs->item.filename = talloc_strdup(cs, filename);
+       for (ci = cs->children; ci; ci = ci->next) {
+               if (ci->type != CONF_ITEM_SECTION) continue;
 
-       /*
-        *      Read the section.  It's OK to have EOF without a
-        *      matching close brace.
-        */
-       if (cf_section_read(cd->name, &lineno, fp, cs) < 0) {
-               fclose(fp);
-               return -1;
+               if (cf_section_pass2(cf_item_to_section(ci)) < 0) return -1;
        }
 
-       fclose(fp);
        return 0;
 }
 
+
 /*
  *     Bootstrap a config file.
  */
@@ -2629,6 +3012,7 @@ int cf_file_read(CONF_SECTION *cs, char const *filename)
 {
        char *p;
        CONF_PAIR *cp;
+       rbtree_t *tree;
 
        cp = cf_pair_alloc(cs, "confdir", filename, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
        if (!cp) return -1;
@@ -2636,12 +3020,23 @@ int cf_file_read(CONF_SECTION *cs, char const *filename)
        p = strrchr(cp->value, FR_DIR_SEP);
        if (p) *p = '\0';
 
-       cp->item.filename = "internal";
+       cp->item.filename = "<internal>";
        cp->item.lineno = -1;
        cf_item_add(cs, &(cp->item));
 
+       tree = rbtree_create(cs, filename_cmp, NULL, 0);
+       if (!tree) return -1;
+
+       cf_data_add_internal(cs, "filename", tree, NULL, 0);
+
        if (cf_file_include(cs, filename) < 0) return -1;
 
+       /*
+        *      Now that we've read the file, go back through it and
+        *      expand the variables.
+        */
+       if (cf_section_pass2(cs) < 0) return -1;
+
        return 0;
 }
 
@@ -2740,12 +3135,12 @@ VALUE_PAIR *cf_pairtovp(CONF_PAIR *pair)
             (pair->rhs_type == T_BACK_QUOTED_STRING))) {
                VALUE_PAIR *vp;
 
-               vp = pairmake(pair, NULL, pair->attr, NULL, pair->op);
+               vp = fr_pair_make(pair, NULL, pair->attr, NULL, pair->op);
                if (!vp) {
                        return NULL;
                }
 
-               if (pairmark_xlat(vp, pair->value) < 0) {
+               if (fr_pair_mark_xlat(vp, pair->value) < 0) {
                        talloc_free(vp);
 
                        return NULL;
@@ -2754,7 +3149,7 @@ VALUE_PAIR *cf_pairtovp(CONF_PAIR *pair)
                return vp;
        }
 
-       return pairmake(pair, NULL, pair->attr, pair->value, pair->op);
+       return fr_pair_make(pair, NULL, pair->attr, pair->value, pair->op);
 }
 
 /*
@@ -3292,7 +3687,7 @@ void cf_log_info(CONF_SECTION const *cs, char const *fmt, ...)
        va_list ap;
 
        va_start(ap, fmt);
-       if ((debug_flag > 1) && cs) vradlog(L_DBG, fmt, ap);
+       if ((rad_debug_lvl > 1) && cs) vradlog(L_DBG, fmt, ap);
        va_end(ap);
 }
 
@@ -3305,7 +3700,7 @@ void cf_log_module(CONF_SECTION const *cs, char const *fmt, ...)
        char buffer[256];
 
        va_start(ap, fmt);
-       if (debug_flag > 1 && cs) {
+       if (rad_debug_lvl > 1 && cs) {
                vsnprintf(buffer, sizeof(buffer), fmt, ap);
 
                DEBUG("%.*s# %s", cs->depth, parse_spaces, buffer);
index d95581b..2df1619 100644 (file)
@@ -26,6 +26,7 @@
 RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/heap.h>
 #include <freeradius-devel/modpriv.h>
 #include <freeradius-devel/rad_assert.h>
 
@@ -47,25 +48,26 @@ static int fr_connection_pool_check(fr_connection_pool_t *pool);
  * @see fr_connection_pool_t
  */
 struct fr_connection {
-       fr_connection_t *prev;          //!< Previous connection in list.
-       fr_connection_t *next;          //!< Next connection in list.
-
-       time_t          created;        //!< Time connection was created.
-       time_t          last_used;      //!< Last time the connection was
-                                       //!< reserved.
-
-       uint32_t        num_uses;       //!< Number of times the connection
-                                       //!< has been 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.
+       fr_connection_t *prev;                  //!< Previous connection in list.
+       fr_connection_t *next;                  //!< Next connection in list.
+
+       time_t          created;                //!< Time connection was created.
+       struct timeval  last_reserved;          //!< Last time the connection was reserved.
+
+       struct timeval  last_released;          //!< Time the connection was released.
+
+       uint32_t        num_uses;               //!< Number of times the connection has been 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.
+
+       int             heap;                   //!< For the next connection heap.
+
 #ifdef PTHREAD_DEBUG
-       pthread_t       pthread_id;     //!< When 'in_use == true'
+       pthread_t       pthread_id;             //!< When 'in_use == true'.
 #endif
 };
 
@@ -79,93 +81,74 @@ struct fr_connection {
  * @see fr_connection
  */
 struct fr_connection_pool_t {
-       uint32_t        start;          //!< Number of initial connections
-       uint32_t        min;            //!< Minimum number of concurrent
-                                       //!< connections to keep open.
-       uint32_t        max;            //!< Maximum number of concurrent
-                                       //!< connections to allow.
-       uint32_t        spare;          //!< Number of spare connections to try
-       uint32_t        pending;        //!< Number of pending open connections
-       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.
-       uint32_t        lifetime;       //!< How long a connection can be open
-                                       //!< before being closed (irrespective
-                                       //!< of whether it's idle or not).
-       uint32_t        idle_timeout;   //!< How long a connection can be idle
-                                       //!< before being closed.
-
-       bool            spread;         //!< If true requests will be spread
-                                       //!< across all connections, instead of
-                                       //!< re-using the most recently used
-                                       //!< connections first.
-
-       time_t          last_checked;   //!< Last time we pruned the connection
-                                       //!< pool.
-       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_throttled; //!< Last time we refused to spawn a
-                                       //!< connection because the last
-                                       //!< connection failed, or we were
-                                       //!< already spawning a connection.
-       time_t          last_at_max;    //!< Last time we hit the maximum number
-                                       //!< of allowed connections.
-
-       uint32_t        max_pending;    //!< Max number of connections to open
-
-       uint64_t        count;          //!< Number of connections spawned over
-                                       //!< the lifetime of the pool.
-       uint32_t        num;            //!< Number of connections in the pool.
-       uint32_t        active;         //!< Number of currently reserved connections.
-
-       fr_connection_t *head;          //!< Start of the connection list.
-       fr_connection_t *tail;          //!< End of the connection list.
+       int             ref;                    //!< Reference counter to prevent connection
+                                               //!< pool being freed multiple times.
+       uint32_t        start;                  //!< Number of initial connections.
+       uint32_t        min;                    //!< Minimum number of concurrent connections to keep open.
+       uint32_t        max;                    //!< Maximum number of concurrent connections to allow.
+       uint32_t        spare;                  //!< Number of spare connections to try.
+       uint32_t        pending;                //!< Number of pending open connections.
+       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.
+       uint32_t        lifetime;               //!< How long a connection can be open before being
+                                               //!< closed (irrespective of whether it's idle or not).
+       uint32_t        idle_timeout;           //!< How long a connection can be idle before
+                                               //!< being closed.
+
+       bool            spread;                 //!< If true we spread requests over the connections,
+                                               //!< using the connection released longest ago, first.
+
+       time_t          last_checked;           //!< Last time we pruned the connection pool.
+       time_t          last_spawned;           //!< Last time we spawned a connection.
+       time_t          last_failed;            //!< Last time we tried to spawn a connection but failed.
+       time_t          last_throttled;         //!< Last time we refused to spawn a connection because
+                                               //!< the last connection failed, or we were already spawning
+                                               //!< a connection.
+       time_t          last_at_max;            //!< Last time we hit the maximum number of allowed
+                                               //!< connections.
+
+       uint32_t        max_pending;            //!< Max number of connections to open.
+
+       uint64_t        count;                  //!< Number of connections spawned over the lifetime
+                                               //!< of the pool.
+       uint32_t        num;                    //!< Number of connections in the pool.
+       uint32_t        active;                 //!< Number of currently reserved connections.
+
+       fr_heap_t       *heap;                  //!< For the next connection heap
+
+       fr_connection_t *head;                  //!< Start of the connection list.
+       fr_connection_t *tail;                  //!< End of the connection list.
 
 #ifdef HAVE_PTHREAD_H
-       pthread_mutex_t mutex;          //!< Mutex used to keep consistent state
-                                       //!< when making modifications in
-                                       //!< threaded mode.
+       pthread_mutex_t mutex;                  //!< Mutex used to keep consistent state when making
+                                               //!< modifications in threaded mode.
 #endif
 
-       CONF_SECTION    *cs;            //!< Configuration section holding
-                                       //!< the section of parsed config file
-                                       //!< that relates to this pool.
-       void            *opaque;        //!< Pointer to context data that will
-                                       //!< be passed to callbacks.
-
-       char const      *log_prefix;    //!< Log prefix to prepend to all log
-                                       //!< messages created by the connection
-                                       //!< pool code.
-
-       char const      *trigger_prefix;        //!< Prefix to prepend to
-                                               //!< names of all triggers
-                                               //!< fired by the connection
-                                               //!< pool code.
-
-       fr_connection_create_t  create; //!< Function used to create new
-                                       //!< connections.
-       fr_connection_alive_t   alive;  //!< Function used to check status
-                                       //!< of connections.
+       CONF_SECTION    *cs;                    //!< Configuration section holding the section of parsed
+                                               //!< config file that relates to this pool.
+       void            *opaque;                //!< Pointer to context data that will be passed to callbacks.
+
+       char const      *log_prefix;            //!< Log prefix to prepend to all log messages created
+                                               //!< by the connection pool code.
+
+       char const      *trigger_prefix;        //!< Prefix to prepend to names of all triggers
+                                               //!< fired by the connection pool code.
+
+       fr_connection_create_t  create;         //!< Function used to create new connections.
+       fr_connection_alive_t   alive;          //!< Function used to check status of connections.
 };
 
 #ifndef HAVE_PTHREAD_H
-#define pthread_mutex_lock(_x)
-#define pthread_mutex_unlock(_x)
+#  define pthread_mutex_lock(_x)
+#  define pthread_mutex_unlock(_x)
 #endif
 
 static const CONF_PARSER connection_config[] = {
@@ -180,9 +163,41 @@ static const CONF_PARSER connection_config[] = {
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
+/** Order connections by reserved most recently
+ */
+static int last_reserved_cmp(void const *one, void const *two)
+{
+       fr_connection_t const *a = one;
+       fr_connection_t const *b = two;
+
+       if (a->last_reserved.tv_sec < b->last_reserved.tv_sec) return -1;
+       if (a->last_reserved.tv_sec > b->last_reserved.tv_sec) return +1;
+
+       if (a->last_reserved.tv_usec < b->last_reserved.tv_usec) return -1;
+       if (a->last_reserved.tv_usec > b->last_reserved.tv_usec) return +1;
+
+       return 0;
+}
+
+/** Order connections by released longest ago
+ */
+static int last_released_cmp(void const *one, void const *two)
+{
+       fr_connection_t const *a = one;
+       fr_connection_t const *b = two;
+
+       if (b->last_released.tv_sec < a->last_released.tv_sec) return -1;
+       if (b->last_released.tv_sec > a->last_released.tv_sec) return +1;
+
+       if (b->last_released.tv_usec < a->last_released.tv_usec) return -1;
+       if (b->last_released.tv_usec > a->last_released.tv_usec) return +1;
+
+       return 0;
+}
+
 /** Removes a connection from the connection list
  *
  * @note Must be called with the mutex held.
@@ -190,8 +205,7 @@ static const CONF_PARSER connection_config[] = {
  * @param[in,out] pool to modify.
  * @param[in] this Connection to delete.
  */
-static void fr_connection_unlink(fr_connection_pool_t *pool,
-                                fr_connection_t *this)
+static void fr_connection_unlink(fr_connection_pool_t *pool, fr_connection_t *this)
 {
        if (this->prev) {
                rad_assert(pool->head != this);
@@ -218,8 +232,7 @@ static void fr_connection_unlink(fr_connection_pool_t *pool,
  * @param[in,out] pool to modify.
  * @param[in] this Connection to add.
  */
-static void fr_connection_link_head(fr_connection_pool_t *pool,
-                                   fr_connection_t *this)
+static void fr_connection_link_head(fr_connection_pool_t *pool, fr_connection_t *this)
 {
        rad_assert(pool != NULL);
        rad_assert(this != NULL);
@@ -241,42 +254,12 @@ static void fr_connection_link_head(fr_connection_pool_t *pool,
        }
 }
 
-/** Adds a connection to the tail of the connection list
- *
- * @note Must be called with the mutex held.
- *
- * @param[in,out] pool to modify.
- * @param[in] this Connection to add.
- */
-static void fr_connection_link_tail(fr_connection_pool_t *pool,
-                                   fr_connection_t *this)
-{
-       rad_assert(pool != NULL);
-       rad_assert(this != NULL);
-       rad_assert(pool->head != this);
-       rad_assert(pool->tail != this);
-
-       if (pool->tail) {
-               pool->tail->next = this;
-       }
-       this->prev = pool->tail;
-       this->next = NULL;
-       pool->tail = this;
-       if (!pool->head) {
-               rad_assert(this->prev == NULL);
-               pool->head = this;
-       } else {
-               rad_assert(this->prev != NULL);
-       }
-}
-
 /** Send a connection pool trigger.
  *
  * @param[in] pool to send trigger for.
  * @param[in] name_suffix trigger name suffix.
  */
-static void fr_connection_exec_trigger(fr_connection_pool_t *pool,
-                                       char const *name_suffix)
+static void fr_connection_exec_trigger(fr_connection_pool_t *pool, char const *name_suffix)
 {
        char name[64];
        rad_assert(pool != NULL);
@@ -285,6 +268,52 @@ static void fr_connection_exec_trigger(fr_connection_pool_t *pool,
        exec_trigger(NULL, pool->cs, name, true);
 }
 
+/** Find a connection handle in the connection list
+ *
+ * Walks over the list of connections searching for a specified connection
+ * handle and returns the first connection that contains that pointer.
+ *
+ * @note Will lock mutex and only release mutex if connection handle
+ * is not found, so will usually return will mutex held.
+ * @note Must be called with the mutex free.
+ *
+ * @param[in] pool to search in.
+ * @param[in] conn handle to search for.
+ * @return
+ *     - Connection containing the specified handle.
+ *     - NULL if non if connection was found.
+ */
+static fr_connection_t *fr_connection_find(fr_connection_pool_t *pool, void *conn)
+{
+       fr_connection_t *this;
+
+       if (!pool || !conn) return NULL;
+
+       pthread_mutex_lock(&pool->mutex);
+
+       /*
+        *      FIXME: This loop could be avoided if we passed a 'void
+        *      **connection' instead.  We could use "offsetof" in
+        *      order to find top of the parent structure.
+        */
+       for (this = pool->head; this != NULL; this = this->next) {
+               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);
+       return NULL;
+}
+
 /** Spawns a new connection
  *
  * Spawns a new connection using the create callback, and returns it for
@@ -296,10 +325,11 @@ static void fr_connection_exec_trigger(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.
+ * @return
+ *     - New connection struct.
+ *     - NULL on error.
  */
-static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
-                                           time_t now, bool in_use)
+static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool, time_t now, bool in_use)
 {
        uint64_t        number;
        uint32_t        max_pending;
@@ -315,9 +345,7 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
         *      opening connections, don't open multiple connections until
         *      we successfully open at least one.
         */
-       if ((pool->num == 0) && pool->pending && pool->last_failed) {
-               return NULL;
-       }
+       if ((pool->num == 0) && pool->pending && pool->last_failed) return NULL;
 
        pthread_mutex_lock(&pool->mutex);
        rad_assert(pool->num <= pool->max);
@@ -409,6 +437,8 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
                pool->pending--;
                pthread_mutex_unlock(&pool->mutex);
 
+               talloc_free(ctx);
+
                return NULL;
        }
 
@@ -421,6 +451,8 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
        this = talloc_zero(pool, fr_connection_t);
        if (!this) {
                pthread_mutex_unlock(&pool->mutex);
+               talloc_free(ctx);
+
                return NULL;
        }
        fr_link_talloc_ctx_free(this, ctx);
@@ -428,9 +460,24 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
        this->created = now;
        this->connection = conn;
        this->in_use = in_use;
+
        this->number = number;
-       this->last_used = now;
+       gettimeofday(&this->last_reserved, NULL);
+       this->last_released = this->last_reserved;
+
+       /*
+        *      The connection pool is starting up.  Insert the
+        *      connection into the heap.
+        */
+       if (!in_use) fr_heap_insert(pool->heap, this);
+
        fr_connection_link_head(pool, this);
+
+       /*
+        *      Do NOT insert the connection into the heap.  That's
+        *      done when the connection is released.
+        */
+
        pool->num++;
 
        rad_assert(pool->pending > 0);
@@ -465,8 +512,7 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
  * @param[in,out] pool to modify.
  * @param[in,out] this Connection to delete.
  */
-static void fr_connection_close(fr_connection_pool_t *pool,
-                               fr_connection_t *this)
+static void fr_connection_close_internal(fr_connection_pool_t *pool, fr_connection_t *this)
 {
        /*
         *      If it's in use, release it.
@@ -476,240 +522,409 @@ static void fr_connection_close(fr_connection_pool_t *pool,
                pthread_t pthread_id = pthread_self();
                rad_assert(pthread_equal(this->pthread_id, pthread_id) != 0);
 #endif
+
                this->in_use = false;
 
                rad_assert(pool->active != 0);
                pool->active--;
+
+       } else {
+               /*
+                *      Connection isn't used, remove it from the heap.
+                */
+               fr_heap_extract(pool->heap, this);
        }
 
        fr_connection_exec_trigger(pool, "close");
 
        fr_connection_unlink(pool, this);
+
        rad_assert(pool->num > 0);
        pool->num--;
        talloc_free(this);
 }
 
-/** Find a connection handle in the connection list
+/** Check whether a connection needs to be removed from the pool
  *
- * Walks over the list of connections searching for a specified connection
- * handle and returns the first connection that contains that pointer.
+ * Will verify that the connection is within idle_timeout, max_uses, and
+ * lifetime values. If it is not, the connection will be closed.
  *
- * @note Will lock mutex and only release mutex if connection handle
- * is not found, so will usually return will mutex held.
- * @note Must be called with the mutex free.
+ * @note Will only close connections not in use.
+ * @note Must be called with the mutex held.
  *
- * @param[in] pool to search in.
- * @param[in] conn handle to search for.
- * @return the connection containing the specified handle, or NULL if non is
- * found.
+ * @param[in,out] pool to modify.
+ * @param[in,out] this Connection to manage.
+ * @param[in] now Current time.
+ * @return
+ *     - 0 if connection was closed.
+ *     - 1 if connection handle was left open.
  */
-static fr_connection_t *fr_connection_find(fr_connection_pool_t *pool, void *conn)
+static int fr_connection_manage(fr_connection_pool_t *pool,
+                               fr_connection_t *this,
+                               time_t now)
 {
-       fr_connection_t *this;
-
-       if (!pool || !conn) return NULL;
-
-       pthread_mutex_lock(&pool->mutex);
+       rad_assert(pool != NULL);
+       rad_assert(this != NULL);
 
        /*
-        *      FIXME: This loop could be avoided if we passed a 'void
-        *      **connection' instead.  We could use "offsetof" in
-        *      order to find top of the parent structure.
+        *      Don't terminated in-use connections
         */
-       for (this = pool->head; this != NULL; this = this->next) {
-               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
+       if (this->in_use) return 1;
 
-                       rad_assert(this->in_use == true);
-                       return this;
+       if ((pool->max_uses > 0) &&
+           (this->num_uses >= pool->max_uses)) {
+               DEBUG("%s: Closing expired connection (%" PRIu64 "): Hit max_uses limit", pool->log_prefix,
+                     this->number);
+       do_delete:
+               if (pool->num <= pool->min) {
+                       DEBUG("%s: You probably need to lower \"min\"", pool->log_prefix);
                }
+               fr_connection_close_internal(pool, this);
+               return 0;
        }
 
-       pthread_mutex_unlock(&pool->mutex);
-       return NULL;
-}
-
-/** Delete a connection from the connection pool.
- *
- * Resolves the connection handle to a connection, then (if found)
- * closes, unlinks and frees that connection.
- *
- * @note Must be called with the mutex free.
- *
- * @param[in,out] pool Connection pool to modify.
- * @param[in] conn to delete.
- * @return 0 if the connection could not be found, else 1.
- */
-int fr_connection_del(fr_connection_pool_t *pool, void *conn)
-{
-       fr_connection_t *this;
-
-       this = fr_connection_find(pool, conn);
-       if (!this) return 0;
+       if ((pool->lifetime > 0) &&
+           ((this->created + pool->lifetime) < now)) {
+               DEBUG("%s: Closing expired connection (%" PRIu64 "): Hit lifetime limit",
+                     pool->log_prefix, this->number);
+               goto do_delete;
+       }
 
-       INFO("%s: Deleting connection (%" PRIu64 ")", pool->log_prefix, this->number);
+       if ((pool->idle_timeout > 0) &&
+           ((this->last_released.tv_sec + pool->idle_timeout) < now)) {
+               INFO("%s: Closing connection (%" PRIu64 "): Hit idle_timeout, was idle for %u seconds",
+                    pool->log_prefix, this->number, (int) (now - this->last_released.tv_sec));
+               goto do_delete;
+       }
 
-       fr_connection_close(pool, this);
-       fr_connection_pool_check(pool);
        return 1;
 }
 
-/** Delete a connection pool
+
+/** Check whether any connections need to be removed from the pool
  *
- * Closes, unlinks and frees all connections in the connection pool, then frees
- * all memory used by the connection pool.
+ * Maintains the number of connections in the pool as per the configuration
+ * parameters for the connection pool.
  *
- * @note Will call the 'stop' trigger.
- * @note Must be called with the mutex free.
+ * @note Will only run checks the first time it's called in a given second,
+ * to throttle connection spawning/closing.
+ * @note Will only close connections not in use.
+ * @note Must be called with the mutex held, will release mutex before
+ * returning.
  *
- * @param[in,out] pool to delete.
+ * @param[in,out] pool to manage.
+ * @return 1
  */
-void fr_connection_pool_delete(fr_connection_pool_t *pool)
+static int fr_connection_pool_check(fr_connection_pool_t *pool)
 {
-       fr_connection_t *this;
-
-       if (!pool) return;
-
-       DEBUG("%s: Removing connection pool", pool->log_prefix);
+       uint32_t spawn, idle, extra;
+       time_t now = time(NULL);
+       fr_connection_t *this, *next;
 
-       pthread_mutex_lock(&pool->mutex);
+       if (pool->last_checked == now) {
+               pthread_mutex_unlock(&pool->mutex);
+               return 1;
+       }
 
        /*
-        *      Don't loop over the list.  Just keep removing the head
-        *      until they're all gone.
+        *      Some idle connections are OK, if they're within the
+        *      configured "spare" range.  Any extra connections
+        *      outside of that range can be closed.
         */
-       while ((this = pool->head) != NULL) {
-               INFO("%s: Closing connection (%" PRIu64 ")", pool->log_prefix, this->number);
-
-               fr_connection_close(pool, this);
+       idle = pool->num - pool->active;
+       if (idle <= pool->spare) {
+               extra = 0;
+       } else {
+               extra = idle - pool->spare;
        }
 
-       fr_connection_exec_trigger(pool, "stop");
-
-       rad_assert(pool->head == NULL);
-       rad_assert(pool->tail == NULL);
-       rad_assert(pool->num == 0);
+       /*
+        *      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->pending) <= pool->min) {
+               spawn = pool->min - (pool->num + pool->pending);
+               extra = 0;
 
-       talloc_free(pool);
-}
+       /*
+        *      If we're about to create more than "max",
+        *      don't create more.
+        */
+       } else if ((pool->num + pool->pending) >= 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 */
 
-/** Initialise a module specific connection pool
- *
- * @see fr_connection_pool_init
- *
- * @param[in] module section.
- * @param[in] opaque data pointer to pass to callbacks.
- * @param[in] c Callback to create new connections.
- * @param[in] a Callback to check the status of connections.
- * @param[in] log_prefix override, if NULL will be set automatically from the module CONF_SECTION.
- * @return A new connection pool or NULL on error.
- */
-fr_connection_pool_t *fr_connection_pool_module_init(CONF_SECTION *module,
-                                                    void *opaque,
-                                                    fr_connection_create_t c,
-                                                    fr_connection_alive_t a,
-                                                    char const *log_prefix)
-{
-       CONF_SECTION *cs, *mycs;
-       char buff[128];
-       char trigger_prefix[64];
-
-       fr_connection_pool_t *pool;
-       char const *cs_name1, *cs_name2;
+       /*
+        *      min < num < max
+        *
+        *      AND we don't have enough idle connections.
+        *      Open some more.
+        */
+       } 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;
 
-       int ret;
+               if ((pool->num + pool->pending + spawn) > pool->max) {
+                       spawn = pool->max - (pool->num + pool->pending);
+               }
 
-#define CONNECTION_POOL_CF_KEY "connection_pool"
-#define parent_name(_x) cf_section_name(cf_item_parent(cf_section_to_item(_x)))
+       /*
+        *      min < num < max
+        *
+        *      We have more than enough idle connections, AND
+        *      some are pending.  Don't open or close any.
+        */
+       } else if (pool->pending) {
+               spawn = 0;
+               extra = 0;
 
-       cs_name1 = cf_section_name1(module);
-       cs_name2 = cf_section_name2(module);
-       if (!cs_name2) cs_name2 = cs_name1;
+       /*
+        *      We have too many idle connections, but closing
+        *      some would take us below "min", so we only
+        *      close enough to take us to "min".
+        */
+       } else if ((pool->min + extra) >= pool->num) {
+               spawn = 0;
+               extra = pool->num - pool->min;
 
-       snprintf(trigger_prefix, sizeof(trigger_prefix), "modules.%s.", cs_name1);
+       } 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 (!log_prefix) {
-               snprintf(buff, sizeof(buff), "rlm_%s (%s)", cs_name1, cs_name2);
-               log_prefix = buff;
+       /*
+        *      Only try to open spares if we're not already attempting to open
+        *      a connection. Avoids spurious log messages.
+        */
+       if (spawn) {
+               INFO("%s: Need %i more connections to reach %i spares",
+                    pool->log_prefix, spawn, pool->spare);
+               pthread_mutex_unlock(&pool->mutex);
+               fr_connection_spawn(pool, now, false); /* ignore return code */
+               pthread_mutex_lock(&pool->mutex);
        }
 
        /*
-        *      Get sibling's pool config section
+        *      We haven't spawned connections in a while, and there
+        *      are too many spare ones.  Close the one which has been
+        *      unused for the longest.
         */
-       ret = find_module_sibling_section(&cs, module, "pool");
-       switch (ret) {
-       case -1:
-               return NULL;
+       if (extra && (now >= (pool->last_spawned + pool->delay_interval))) {
+               fr_connection_t *found;
 
-       case 1:
-               DEBUG4("%s: Using pool section from \"%s\"", log_prefix, parent_name(cs));
-               break;
+               found = NULL;
+               for (this = pool->tail; this != NULL; this = this->prev) {
+                       if (this->in_use) continue;
 
-       case 0:
-               DEBUG4("%s: Using local pool section", log_prefix);
-               break;
+                       if (!found ||
+                           timercmp(&this->last_reserved, &found->last_reserved, <)) {
+                               found = this;
+                       }
+               }
+
+               rad_assert(found != NULL);
+
+               INFO("%s: Closing connection (%" PRIu64 "), from %d unused connections", pool->log_prefix,
+                    found->number, extra);
+               fr_connection_close_internal(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;
        }
 
        /*
-        *      Get our pool config section
+        *      Pass over all of the connections in the pool, limiting
+        *      lifetime, idle time, max requests, etc.
         */
-       mycs = cf_section_sub_find(module, "pool");
-       if (!mycs) {
-               DEBUG4("%s: Adding pool section to config item \"%s\" to store pool references", log_prefix,
-                      cf_section_name(module));
-
-               mycs = cf_section_alloc(module, "pool", NULL);
-               cf_section_add(module, mycs);
+       for (this = pool->head; this != NULL; this = next) {
+               next = this->next;
+               fr_connection_manage(pool, this, now);
        }
 
+       pool->last_checked = now;
+       pthread_mutex_unlock(&pool->mutex);
+
+       return 1;
+}
+
+/** Get a connection from the connection pool
+ *
+ * @note Must be called with the mutex free.
+ *
+ * @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.
+ *     - NULL on error.
+ */
+static void *fr_connection_get_internal(fr_connection_pool_t *pool, bool spawn)
+{
+       time_t now;
+       fr_connection_t *this;
+
+       if (!pool) return NULL;
+
+#ifdef HAVE_PTHREAD_H
+       if (spawn) pthread_mutex_lock(&pool->mutex);
+#endif
+
+       now = time(NULL);
+
        /*
-        *      Sibling didn't have a pool config section
-        *      Use our own local pool.
+        *      Grab the link with the lowest latency, and check it
+        *      for limits.  If "connection manage" says the link is
+        *      no longer usable, go grab another one.
         */
-       if (!cs) {
-               DEBUG4("%s: \"%s.pool\" section not found, using \"%s.pool\"", log_prefix,
-                      parent_name(cs), parent_name(mycs));
-               cs = mycs;
+       do {
+               this = fr_heap_peek(pool->heap);
+               if (!this) break;
+       } while (!fr_connection_manage(pool, this, now));
+
+       /*
+        *      We have a working connection.  Extract it from the
+        *      heap and use it.
+        */
+       if (this) {
+               fr_heap_extract(pool->heap, this);
+               goto do_return;
        }
 
        /*
-        *      If fr_connection_pool_init has already been called
-        *      for this config section, reuse the previous instance.
-        *
-        *      This allows modules to pass in the config sections
-        *      they would like to use the connection pool from.
+        *      We were asked to avoid spawning a new connection, by
+        *      fr_connection_reconnect_internal().  So we just return
+        *      here.
         */
-       pool = cf_data_find(cs, CONNECTION_POOL_CF_KEY);
-       if (!pool) {
-               DEBUG4("%s: No pool reference found for config item \"%s.pool\"", log_prefix, parent_name(cs));
-               pool = fr_connection_pool_init(module, cs, opaque, c, a, log_prefix, trigger_prefix);
-               if (!pool) return NULL;
+       if (!spawn) return NULL;
 
-               DEBUG4("%s: Adding pool reference %p to config item \"%s.pool\"", log_prefix, pool, parent_name(cs));
-               cf_data_add(cs, CONNECTION_POOL_CF_KEY, pool, NULL);
-               return pool;
+       /*
+        *      We don't have a connection.  Try to open a new one.
+        */
+       rad_assert(pool->active == pool->num);
+
+       if (pool->num == pool->max) {
+               bool complain = false;
+
+               /*
+                *      Rate-limit complaints.
+                */
+               if (pool->last_at_max != now) {
+                       complain = true;
+                       pool->last_at_max = now;
+               }
+
+               pthread_mutex_unlock(&pool->mutex);
+               
+               if (!RATE_LIMIT_ENABLED || complain) {
+                       ERROR("%s: No connections available and at max connection limit", pool->log_prefix);
+               }
+
+               return NULL;
        }
 
-       DEBUG4("%s: Found pool reference %p in config item \"%s.pool\"", log_prefix, pool, parent_name(cs));
+       pthread_mutex_unlock(&pool->mutex);
+
+       DEBUG("%s: %i of %u connections in use.  You  may need to increase \"spare\"", pool->log_prefix,
+             pool->active, pool->num);
+       this = fr_connection_spawn(pool, now, true); /* MY connection! */
+       if (!this) return NULL;
+
+       pthread_mutex_lock(&pool->mutex);
+
+do_return:
+       pool->active++;
+       this->num_uses++;
+       gettimeofday(&this->last_reserved, NULL);
+       this->in_use = true;
+
+#ifdef PTHREAD_DEBUG
+       this->pthread_id = pthread_self();
+#endif
+
+#ifdef HAVE_PTHREAD_H
+       if (spawn) pthread_mutex_unlock(&pool->mutex);
+#endif
+
+       DEBUG("%s: Reserved connection (%" PRIu64 ")", pool->log_prefix, this->number);
+
+       return this->connection;
+}
+
+/** Reconnect a suspected inviable connection
+ *
+ * @note Must be called with the mutex held, will not release mutex.
+ *
+ * @see fr_connection_get
+ * @param[in,out] pool to reconnect the connection in.
+ * @param[in,out] conn to reconnect.
+ * @return new connection handle if successful else NULL.
+ */
+static fr_connection_t *fr_connection_reconnect_internal(fr_connection_pool_t *pool, fr_connection_t *conn)
+{
+       void            *new_conn;
+       uint64_t        conn_number;
+       TALLOC_CTX      *ctx;
+
+       conn_number = conn->number;
 
        /*
-        *      We're reusing pool data add it to our local config
-        *      section. This allows other modules to transitively
-        *      re-use a pool through this module.
+        *      Destroy any handles associated with the fr_connection_t
         */
-       if (mycs != cs) {
-               DEBUG4("%s: Copying pool reference %p from config item \"%s.pool\" to config item \"%s.pool\"",
-                      log_prefix, pool, parent_name(cs), parent_name(mycs));
-               cf_data_add(mycs, CONNECTION_POOL_CF_KEY, pool, NULL);
+       talloc_free_children(conn);
+
+       DEBUG("%s: Reconnecting (%" PRIu64 ")", pool->log_prefix, conn_number);
+
+       /*
+        *      Allocate a new top level ctx for the create callback
+        *      to hang its memory off of.
+        */
+       ctx = talloc_init("fr_connection_ctx");
+       if (!ctx) return NULL;
+       fr_link_talloc_ctx_free(conn, ctx);
+
+       new_conn = pool->create(ctx, pool->opaque);
+       if (!new_conn) {
+               /*
+                *      We can't create a new connection, so close the current one.
+                */
+               fr_connection_close_internal(pool, conn);
+
+               /*
+                *      Maybe there's a connection which is unused and
+                *      available.  If so, return it.
+                */
+               new_conn = fr_connection_get_internal(pool, false);
+               if (new_conn) return new_conn;
+
+               RATE_LIMIT(ERROR("%s: Failed to reconnect (%" PRIu64 "), no free connections are available",
+                                pool->log_prefix, conn_number));
+
+               return NULL;
        }
 
-       return pool;
+       fr_connection_exec_trigger(pool, "close");
+       conn->connection = new_conn;
+
+       return new_conn;
 }
 
 /** Create a new connection pool
@@ -722,16 +937,18 @@ fr_connection_pool_t *fr_connection_pool_module_init(CONF_SECTION *module,
  *
  * @note Will call the 'start' trigger.
  *
- * @param[in] parent section.
+ * @param[in] ctx Context to link pool's destruction to.
  * @param[in] cs pool section.
  * @param[in] opaque data pointer to pass to callbacks.
  * @param[in] c Callback to create new connections.
  * @param[in] a Callback to check the status of connections.
  * @param[in] log_prefix prefix to prepend to all log messages.
  * @param[in] trigger_prefix prefix to prepend to all trigger names.
- * @return A new connection pool or NULL on error.
+ * @return
+ *     - New connection pool.
+ *     - NULL on error.
  */
-fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
+fr_connection_pool_t *fr_connection_pool_init(TALLOC_CTX *ctx,
                                              CONF_SECTION *cs,
                                              void *opaque,
                                              fr_connection_create_t c,
@@ -744,7 +961,7 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
        fr_connection_t *this;
        time_t now;
 
-       if (!parent || !cs || !opaque || !c) return NULL;
+       if (!cs || !opaque || !c) return NULL;
 
        now = time(NULL);
 
@@ -760,7 +977,7 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
         *      Ensure the pool is freed at the same time
         *      as its parent.
         */
-       if (fr_link_talloc_ctx_free(cs, pool) < 0) {
+       if (fr_link_talloc_ctx_free(ctx, pool) < 0) {
                talloc_free(pool);
 
                return NULL;
@@ -773,9 +990,54 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
 
        pool->head = pool->tail = NULL;
 
+       /*
+        *      We keep a heap of connections, sorted by the last time
+        *      we STARTED using them.  Newly opened connections
+        *      aren't in the heap.  They're only inserted in the list
+        *      once they're released.
+        *
+        *      We do "most recently started" instead of "most
+        *      recently used", because MRU is done as most recently
+        *      *released*.  We want to order connections by
+        *      responsiveness, and MRU prioritizes high latency
+        *      connections.
+        *
+        *      We want most recently *started*, which gives
+        *      preference to low latency links, and pushes high
+        *      latency links down in the priority heap.
+        *
+        *      https://code.facebook.com/posts/1499322996995183/solving-the-mystery-of-link-imbalance-a-metastable-failure-state-at-scale/
+        */
+       if (!pool->spread) {
+               pool->heap = fr_heap_create(last_reserved_cmp, offsetof(fr_connection_t, heap));
+       /*
+        *      For some types of connections we need to used a different
+        *      algorithm, because load balancing benefits are secondary
+        *      to maintaining a cache of open connections.
+        *
+        *      With libcurl's multihandle, connections can only be reused
+        *      if all handles that make up the multhandle are done processing
+        *      their requests.
+        *
+        *      We can't tell when that's happened using libcurl, and even
+        *      if we could, blocking until all servers had responded
+        *      would have huge cost.
+        *
+        *      The solution is to order the heap so that the connection that
+        *      was released longest ago is at the top.
+        *
+        *      That way we maximise time between connection use.
+        */
+       } else {
+               pool->heap = fr_heap_create(last_released_cmp, offsetof(fr_connection_t, heap));
+       }
+       if (!pool->heap) {
+               talloc_free(pool);
+               return NULL;
+       }
+
        pool->log_prefix = log_prefix ? talloc_typed_strdup(pool, log_prefix) : "core";
-       pool->trigger_prefix = trigger_prefix ?
-                                       talloc_typed_strdup(pool, trigger_prefix) : "";
+       pool->trigger_prefix = trigger_prefix ? talloc_typed_strdup(pool, trigger_prefix) : "";
 
 #ifdef HAVE_PTHREAD_H
        pthread_mutex_init(&pool->mutex, NULL);
@@ -829,7 +1091,7 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
                this = fr_connection_spawn(pool, now, false);
                if (!this) {
                error:
-                       fr_connection_pool_delete(pool);
+                       fr_connection_pool_free(pool);
                        return NULL;
                }
        }
@@ -839,298 +1101,202 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
        return pool;
 }
 
-
-/** Check whether a connection needs to be removed from the pool
- *
- * Will verify that the connection is within idle_timeout, max_uses, and
- * lifetime values. If it is not, the connection will be closed.
+/** Initialise a module specific connection pool
  *
- * @note Will only close connections not in use.
- * @note Must be called with the mutex held.
+ * @see fr_connection_pool_init
  *
- * @param[in,out] pool to modify.
- * @param[in,out] this Connection to manage.
- * @param[in] now Current time.
- * @return 0 if the connection was closed, otherwise 1.
+ * @param[in] module section.
+ * @param[in] opaque data pointer to pass to callbacks.
+ * @param[in] c Callback to create new connections.
+ * @param[in] a Callback to check the status of connections.
+ * @param[in] log_prefix override, if NULL will be set automatically from the module CONF_SECTION.
+ * @return
+ *     - New connection pool.
+ *     - NULL on error.
  */
-static int fr_connection_manage(fr_connection_pool_t *pool,
-                               fr_connection_t *this,
-                               time_t now)
+fr_connection_pool_t *fr_connection_pool_module_init(CONF_SECTION *module,
+                                                    void *opaque,
+                                                    fr_connection_create_t c,
+                                                    fr_connection_alive_t a,
+                                                    char const *log_prefix)
 {
-       rad_assert(pool != NULL);
-       rad_assert(this != NULL);
-
-       /*
-        *      Don't terminated in-use connections
-        */
-       if (this->in_use) return 1;
-
-       if ((pool->max_uses > 0) &&
-           (this->num_uses >= pool->max_uses)) {
-               DEBUG("%s: Closing expired connection (%" PRIu64 "): Hit max_uses limit", pool->log_prefix,
-                     this->number);
-       do_delete:
-               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;
-       }
-
-       if ((pool->lifetime > 0) &&
-           ((this->created + pool->lifetime) < now)) {
-               DEBUG("%s: Closing expired connection (%" PRIu64 "): Hit lifetime limit",
-                     pool->log_prefix, this->number);
-               goto do_delete;
-       }
+       CONF_SECTION *cs, *mycs;
+       char buff[128];
+       char trigger_prefix[64];
 
-       if ((pool->idle_timeout > 0) &&
-           ((this->last_used + pool->idle_timeout) < now)) {
-               INFO("%s: Closing connection (%" PRIu64 "): Hit idle_timeout, was idle for %u seconds",
-                    pool->log_prefix, this->number, (int) (now - this->last_used));
-               goto do_delete;
-       }
+       fr_connection_pool_t *pool;
+       char const *cs_name1, *cs_name2;
 
-       return 1;
-}
+       int ret;
 
+#define CONNECTION_POOL_CF_KEY "connection_pool"
+#define parent_name(_x) cf_section_name(cf_item_parent(cf_section_to_item(_x)))
 
-/** Check whether any connections need to be removed from the pool
- *
- * Maintains the number of connections in the pool as per the configuration
- * parameters for the connection pool.
- *
- * @note Will only run checks the first time it's called in a given second,
- * to throttle connection spawning/closing.
- * @note Will only close connections not in use.
- * @note Must be called with the mutex held, will release mutex before
- * returning.
- *
- * @param[in,out] pool to manage.
- * @return 1
- */
-static int fr_connection_pool_check(fr_connection_pool_t *pool)
-{
-       uint32_t spawn, idle, extra;
-       time_t now = time(NULL);
-       fr_connection_t *this, *next;
+       cs_name1 = cf_section_name1(module);
+       cs_name2 = cf_section_name2(module);
+       if (!cs_name2) cs_name2 = cs_name1;
 
-       if (pool->last_checked == now) {
-               pthread_mutex_unlock(&pool->mutex);
-               return 1;
-       }
+       snprintf(trigger_prefix, sizeof(trigger_prefix), "modules.%s.", cs_name1);
 
-       /*
-        *      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 (!log_prefix) {
+               snprintf(buff, sizeof(buff), "rlm_%s (%s)", cs_name1, cs_name2);
+               log_prefix = buff;
        }
 
        /*
-        *      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->pending) <= pool->min) {
-               spawn = pool->min - (pool->num + pool->pending);
-               extra = 0;
-
-       /*
-        *      If we're about to create more than "max",
-        *      don't create more.
-        */
-       } else if ((pool->num + pool->pending) >= 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 */
-
-       /*
-        *      min < num < max
-        *
-        *      AND we don't have enough idle connections.
-        *      Open some more.
+        *      Get sibling's pool config section
         */
-       } 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 ((pool->num + pool->pending + spawn) > pool->max) {
-                       spawn = pool->max - (pool->num + pool->pending);
-               }
+       ret = find_module_sibling_section(&cs, module, "pool");
+       switch (ret) {
+       case -1:
+               return NULL;
 
-       /*
-        *      min < num < max
-        *
-        *      We have more than enough idle connections, AND
-        *      some are pending.  Don't open or close any.
-        */
-       } else if (pool->pending) {
-               spawn = 0;
-               extra = 0;
+       case 1:
+               DEBUG4("%s: Using pool section from \"%s\"", log_prefix, parent_name(cs));
+               break;
+
+       case 0:
+               DEBUG4("%s: Using local pool section", log_prefix);
+               break;
+       }
 
        /*
-        *      We have too many idle connections, but closing
-        *      some would take us below "min", so we only
-        *      close enough to take us to "min".
+        *      Get our pool config section
         */
-       } else if ((pool->min + extra) >= pool->num) {
-               spawn = 0;
-               extra = pool->num - pool->min;
+       mycs = cf_section_sub_find(module, "pool");
+       if (!mycs) {
+               DEBUG4("%s: Adding pool section to config item \"%s\" to store pool references", log_prefix,
+                      cf_section_name(module));
 
-       } 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 */
+               mycs = cf_section_alloc(module, "pool", NULL);
+               cf_section_add(module, mycs);
        }
 
        /*
-        *      Only try to open spares if we're not already attempting to open
-        *      a connection. Avoids spurious log messages.
+        *      Sibling didn't have a pool config section
+        *      Use our own local pool.
         */
-       if (spawn) {
-               INFO("%s: %i of %u connections in use.  Need more spares", pool->log_prefix, pool->active, pool->num);
-               pthread_mutex_unlock(&pool->mutex);
-               fr_connection_spawn(pool, now, false); /* ignore return code */
-               pthread_mutex_lock(&pool->mutex);
+       if (!cs) {
+               DEBUG4("%s: \"%s.pool\" section not found, using \"%s.pool\"", log_prefix,
+                      parent_name(cs), parent_name(mycs));
+               cs = mycs;
        }
 
        /*
-        *      We haven't spawned connections in a while, and there
-        *      are too many spare ones.  Close the one which has been
-        *      unused for the longest.
+        *      If fr_connection_pool_init has already been called
+        *      for this config section, reuse the previous instance.
+        *
+        *      This allows modules to pass in the config sections
+        *      they would like to use the connection pool from.
         */
-       if (extra && (now >= (pool->last_spawned + pool->delay_interval))) {
-               fr_connection_t *found;
-
-               found = NULL;
-               for (this = pool->tail; this != NULL; this = this->prev) {
-                       if (this->in_use) continue;
-
-                       if (!found ||
-                          (this->last_used < found->last_used)) {
-                               found = this;
-                       }
-               }
-
-               rad_assert(found != NULL);
-
-               INFO("%s: Closing connection (%" PRIu64 "), from %d unused connections", pool->log_prefix,
-                    found->number, extra);
-               fr_connection_close(pool, found);
+       pool = cf_data_find(cs, CONNECTION_POOL_CF_KEY);
+       if (!pool) {
+               DEBUG4("%s: No pool reference found for config item \"%s.pool\"", log_prefix, parent_name(cs));
+               pool = fr_connection_pool_init(cs, cs, opaque, c, a, log_prefix, trigger_prefix);
+               if (!pool) return NULL;
 
-               /*
-                *      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;
+               DEBUG4("%s: Adding pool reference %p to config item \"%s.pool\"", log_prefix, pool, parent_name(cs));
+               cf_data_add(cs, CONNECTION_POOL_CF_KEY, pool, NULL);
+               return pool;
        }
+       pool->ref++;
+
+       DEBUG4("%s: Found pool reference %p in config item \"%s.pool\"", log_prefix, pool, parent_name(cs));
 
        /*
-        *      Pass over all of the connections in the pool, limiting
-        *      lifetime, idle time, max requests, etc.
+        *      We're reusing pool data add it to our local config
+        *      section. This allows other modules to transitively
+        *      re-use a pool through this module.
         */
-       for (this = pool->head; this != NULL; this = next) {
-               next = this->next;
-               fr_connection_manage(pool, this, now);
+       if (mycs != cs) {
+               DEBUG4("%s: Copying pool reference %p from config item \"%s.pool\" to config item \"%s.pool\"",
+                      log_prefix, pool, parent_name(cs), parent_name(mycs));
+               cf_data_add(mycs, CONNECTION_POOL_CF_KEY, pool, NULL);
        }
 
-       pool->last_checked = now;
-       pthread_mutex_unlock(&pool->mutex);
+       return pool;
+}
 
-       return 1;
+/** Allocate a new pool using an existing one as a template
+ *
+ * @param ctx to allocate new pool in.
+ * @param pool to copy.
+ * @param opaque data to pass to connection function.
+ * @return
+ *     - New connection pool.
+ *     - NULL on error.
+ */
+fr_connection_pool_t *fr_connection_pool_copy(TALLOC_CTX *ctx, fr_connection_pool_t *pool, void *opaque)
+{
+       return fr_connection_pool_init(ctx, pool->cs, opaque, pool->create,
+                                      pool->alive, pool->log_prefix, pool->trigger_prefix);
 }
 
-/** Get a connection from the connection pool
+/** Get the number of connections currently in the pool
  *
- * @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.
+ * @param pool to count connections for.
+ * @return the number of connections in the pool
  */
-static void *fr_connection_get_internal(fr_connection_pool_t *pool, bool spawn)
+int fr_connection_pool_get_num(fr_connection_pool_t *pool)
 {
-       time_t now;
-       fr_connection_t *this, *next;
+       return pool->num;
+}
 
-       if (!pool) return NULL;
 
-       pthread_mutex_lock(&pool->mutex);
+/** Delete a connection pool
+ *
+ * Closes, unlinks and frees all connections in the connection pool, then frees
+ * all memory used by the connection pool.
+ *
+ * @note Will call the 'stop' trigger.
+ * @note Must be called with the mutex free.
+ *
+ * @param[in,out] pool to delete.
+ */
+void fr_connection_pool_free(fr_connection_pool_t *pool)
+{
+       fr_connection_t *this;
 
-       now = time(NULL);
-       for (this = pool->head; this != NULL; this = next) {
-               next = this->next;
-               if (!fr_connection_manage(pool, this, now)) continue;
+       if (!pool) return;
 
-               if (!this->in_use) goto do_return;
+       /*
+        *      More modules hold a reference to this pool, don't free
+        *      it yet.
+        */
+       if (pool->ref > 0) {
+               pool->ref--;
+               return;
        }
-       rad_assert(pool->active == pool->num);
-
-       if (pool->num == pool->max) {
-               bool complain = false;
 
-               /*
-                *      Rate-limit complaints.
-                */
-               if (pool->last_at_max != now) {
-                       complain = true;
-                       pool->last_at_max = now;
-               }
+       DEBUG("%s: Removing connection pool", pool->log_prefix);
 
-               pthread_mutex_unlock(&pool->mutex);
+       pthread_mutex_lock(&pool->mutex);
 
-               if (!RATE_LIMIT_ENABLED || complain) {
-                       ERROR("%s: No connections available and at max connection limit", pool->log_prefix);
-               }
+       /*
+        *      Don't loop over the list.  Just keep removing the head
+        *      until they're all gone.
+        */
+       while ((this = pool->head) != NULL) {
+               INFO("%s: Closing connection (%" PRIu64 ")", pool->log_prefix, this->number);
 
-               return NULL;
+               fr_connection_close_internal(pool, this);
        }
 
-       pthread_mutex_unlock(&pool->mutex);
-
-       if (!spawn) return NULL;
+       fr_heap_delete(pool->heap);
 
-       DEBUG("%s: %i of %u connections in use.  You  may need to increase \"spare\"", pool->log_prefix,
-             pool->active, pool->num);
-       this = fr_connection_spawn(pool, now, true); /* MY connection! */
-       if (!this) return NULL;
-       pthread_mutex_lock(&pool->mutex);
+       fr_connection_exec_trigger(pool, "stop");
 
-do_return:
-       pool->active++;
-       this->num_uses++;
-       this->last_used = now;
-       this->in_use = true;
+       rad_assert(pool->head == NULL);
+       rad_assert(pool->tail == NULL);
+       rad_assert(pool->num == 0);
 
-#ifdef PTHREAD_DEBUG
-       this->pthread_id = pthread_self();
+#ifdef HAVE_PTHREAD_H
+       pthread_mutex_destroy(&pool->mutex);
 #endif
-       pthread_mutex_unlock(&pool->mutex);
-
-       DEBUG("%s: Reserved connection (%" PRIu64 ")", pool->log_prefix, this->number);
 
-       return this->connection;
+       talloc_free(pool);
 }
 
-
 /** Reserve a connection in the connection pool
  *
  * Will attempt to find an unused connection in the connection pool, if one is
@@ -1146,23 +1312,15 @@ do_return:
  *
  * @see fr_connection_release
  * @param[in,out] pool to reserve the connection from.
- * @return a pointer to the connection handle, or NULL on error.
+ * @return
+ *     - A pointer to the connection handle.
+ *     - 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
@@ -1182,28 +1340,19 @@ void fr_connection_release(fr_connection_pool_t *pool, void *conn)
        this->in_use = false;
 
        /*
-        *      Determines whether the last used connection gets
-        *      re-used first.
+        *      Record when the connection was last released
         */
-       if (pool->spread) {
-               /*
-                *      Put it at the tail of the list, so
-                *      that it will get re-used last.
-                */
-               if (this != pool->tail) {
-                       fr_connection_unlink(pool, this);
-                       fr_connection_link_tail(pool, this);
-               }
-       } else {
-               /*
-                *      Put it at the head of the list, so
-                *      that it will get re-used quickly.
-                */
-               if (this != pool->head) {
-                       fr_connection_unlink(pool, this);
-                       fr_connection_link_head(pool, this);
-               }
-       }
+       gettimeofday(&this->last_released, NULL);
+
+       /*
+        *      Insert the connection in the heap.
+        *
+        *      This will either be based on when we *started* using it
+        *      (allowing fast links to be re-used, and slow links to be
+        *      gradually expired), or when we released it (allowing
+        *      the maximum amount of time between connection use).
+        */
+       fr_heap_insert(pool->heap, this);
 
        rad_assert(pool->active != 0);
        pool->active--;
@@ -1236,8 +1385,12 @@ void fr_connection_release(fr_connection_pool_t *pool, void *conn)
  * and a function higher up the stack doesn't attempt to use a now invalid
  * connection handle.
  *
+ * @note Will free any talloced memory hung off the context of the connection,
+ *     being reconnected.
+ *
  * @warning After calling reconnect the caller *MUST NOT* attempt to use
- * the old handle in any other operations, as its memory will have been freed.
+ *     the old handle in any other operations, as its memory will have been
+ *     freed.
  *
  * @see fr_connection_get
  * @param[in,out] pool to reconnect the connection in.
@@ -1246,10 +1399,8 @@ void fr_connection_release(fr_connection_pool_t *pool, void *conn)
  */
 void *fr_connection_reconnect(fr_connection_pool_t *pool, void *conn)
 {
-       void *new_conn;
-       fr_connection_t *this;
-       uint64_t conn_number;
-       TALLOC_CTX *ctx;
+       void            *new_conn;
+       fr_connection_t *this;
 
        if (!pool || !conn) return NULL;
 
@@ -1259,49 +1410,35 @@ void *fr_connection_reconnect(fr_connection_pool_t *pool, void *conn)
        this = fr_connection_find(pool, conn);
        if (!this) return NULL;
 
+       new_conn = fr_connection_reconnect_internal(pool, this);
+       pthread_mutex_unlock(&pool->mutex);
 
-       conn_number = this->number;
-
-       /*
-        *      Destroy any handles associated with the fr_connection_t
-        */
-       talloc_free_children(this);
-
-       DEBUG("%s: Reconnecting (%" PRIu64 ")", pool->log_prefix, conn_number);
-
-       /*
-        *      Allocate a new top level ctx for the create callback
-        *      to hang its memory off of.
-        */
-       ctx = talloc_init("fr_connection_ctx");
-       if (!ctx) return NULL;
-       fr_link_talloc_ctx_free(this, ctx);
-
-       new_conn = pool->create(ctx, pool->opaque);
-       if (!new_conn) {
-               /*
-                *      We can't create a new connection, so close
-                *      this one.
-                */
-               fr_connection_close(pool, this);
-
-               /*
-                *      Maybe there's a connection which is unused and
-                *      available.  If so, return it.
-                */
-               pthread_mutex_unlock(&pool->mutex);
-               new_conn = fr_connection_get_internal(pool, false);
-               if (new_conn) return new_conn;
+       return new_conn;
+}
 
-               RATE_LIMIT(ERROR("%s: Failed to reconnect (%" PRIu64 "), no free connections are available",
-                                pool->log_prefix, conn_number));
+/** Delete a connection from the connection pool.
+ *
+ * Resolves the connection handle to a connection, then (if found)
+ * closes, unlinks and frees that connection.
+ *
+ * @note Must be called with the mutex free.
+ *
+ * @param[in,out] pool Connection pool to modify.
+ * @param[in] conn to delete.
+ * @return
+ *     - 0 If the connection could not be found.
+ *     - 1 if the connection was deleted.
+ */
+int fr_connection_close(fr_connection_pool_t *pool, void *conn)
+{
+       fr_connection_t *this;
 
-               return NULL;
-       }
+       this = fr_connection_find(pool, conn);
+       if (!this) return 0;
 
-       fr_connection_exec_trigger(pool, "close");
-       this->connection = new_conn;
-       pthread_mutex_unlock(&pool->mutex);
+       INFO("%s: Deleting connection (%" PRIu64 ")", pool->log_prefix, this->number);
 
-       return new_conn;
+       fr_connection_close_internal(pool, this);
+       fr_connection_pool_check(pool);
+       return 1;
 }
index 808cee5..da52235 100644 (file)
@@ -80,8 +80,8 @@ int detail_send(rad_listen_t *listener, REQUEST *request)
                data->signal = 1;
                data->state = STATE_NO_REPLY;
 
-               RDEBUG("Detail - No response to request.  Will retry in %d seconds",
-                      data->retry_interval);
+               RDEBUG("detail (%s): No response to request.  Will retry in %d seconds",
+                      data->name, data->retry_interval);
        } else {
                int rtt;
                struct timeval now;
@@ -152,8 +152,8 @@ int detail_send(rad_listen_t *listener, REQUEST *request)
                 */
                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("detail (%s): Received response for request %d.  Will read the next packet in %d seconds",
+                       data->name, request->number, data->delay_time / USEC);
 
                data->last_packet = now;
                data->signal = 1;
@@ -163,7 +163,7 @@ int detail_send(rad_listen_t *listener, REQUEST *request)
 
 #ifdef WITH_DETAIL_THREAD
        if (write(data->child_pipe[1], &c, 1) < 0) {
-               ERROR("Failed writing ack to reader thread: %s", fr_syserror(errno));
+               RERROR("detail (%s): Failed writing ack to reader thread: %s", data->name, fr_syserror(errno));
        }
 #else
        radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL);
@@ -201,6 +201,7 @@ static int detail_open(rad_listen_t *this)
         *      this file will be read && processed before the
         *      file globbing is done.
         */
+       data->fp = NULL;
        data->work_fd = open(data->filename_work, O_RDWR);
        if (data->work_fd < 0) {
 #ifndef HAVE_GLOB_H
@@ -212,7 +213,7 @@ static int detail_open(rad_listen_t *this)
                char const      *filename;
                glob_t          files;
 
-               DEBUG2("Polling for detail file %s", data->filename);
+               DEBUG2("detail (%s): Polling for detail file", data->name);
 
                memset(&files, 0, sizeof(files));
                if (glob(data->filename, 0, NULL, &files) != 0) {
@@ -243,10 +244,10 @@ static int detail_open(rad_listen_t *this)
                 */
                filename = files.gl_pathv[found];
 
-               DEBUG("Detail - Renaming %s -> %s", filename, data->filename_work);
+               DEBUG("detail (%s): Renaming %s -> %s", data->name, filename, data->filename_work);
                if (rename(filename, data->filename_work) < 0) {
-                       ERROR("Detail - Failed renaming %s to %s: %s",
-                             filename, data->filename_work, fr_syserror(errno));
+                       ERROR("detail (%s): Failed renaming %s to %s: %s",
+                             data->name, filename, data->filename_work, fr_syserror(errno));
                        goto noop;
                }
 
@@ -309,6 +310,19 @@ int detail_recv(rad_listen_t *listener)
        packet = detail_poll(listener);
        if (!packet) return -1;
 
+       if (DEBUG_ENABLED2) {
+               VALUE_PAIR *vp;
+               vp_cursor_t cursor;
+
+               DEBUG2("detail (%s): Read packet from %s", data->name, data->filename_work);
+
+               for (vp = fr_cursor_init(&cursor, &packet->vps);
+                    vp;
+                    vp = fr_cursor_next(&cursor)) {
+                       debug_pair(vp);
+               }
+       }
+
        switch (packet->code) {
        case PW_CODE_ACCOUNTING_REQUEST:
                fun = rad_accounting;
@@ -328,8 +342,7 @@ int detail_recv(rad_listen_t *listener)
        /*
         *      Don't bother doing limit checks, etc.
         */
-       if (!request_receive(NULL, listener, packet, &data->detail_client,
-                            fun)) {
+       if (!request_receive(NULL, listener, packet, &data->detail_client, fun)) {
                rad_free(&packet);
                data->state = STATE_NO_REPLY;   /* try again later */
                return 0;
@@ -352,6 +365,17 @@ int detail_recv(rad_listen_t *listener)
        rcode = read(data->master_pipe[0], &packet, sizeof(packet));
        if (rcode <= 0) return rcode;
 
+       if (DEBUG_ENABLED2) {
+               VALUE_PAIR *vp;
+               vp_cursor_t cursor;
+
+               DEBUG2("detail (%s): Read packet from %s", data->name, data->filename_work);
+               for (vp = fr_cursor_init(&cursor, &packet->vps);
+                    vp;
+                    vp = fr_cursor_next(&cursor)) {
+                       debug_pair(vp);
+               }
+       }
        rad_assert(packet != NULL);
 
        switch (packet->code) {
@@ -369,14 +393,14 @@ int detail_recv(rad_listen_t *listener)
                goto signal_thread;
        }
 
-       if (!request_receive(NULL, listener, packet, &data->detail_client,
-                            fun)) {
+       if (!request_receive(NULL, listener, packet, &data->detail_client, fun)) {
                data->state = STATE_NO_REPLY;   /* try again later */
 
        signal_thread:
                rad_free(&packet);
                if (write(data->child_pipe[1], &c, 1) < 0) {
-                       ERROR("Failed writing ack to reader thread: %s", fr_syserror(errno));
+                       ERROR("detail (%s): Failed writing ack to reader thread: %s", data->name,
+                             fr_syserror(errno));
                }
        }
 
@@ -408,12 +432,12 @@ open_file:
 
                /* FALL-THROUGH */
 
-               /*
-                *      Try to lock fd.  If we can't, return.
-                *      If we can, continue.  This means that
-                *      the server doesn't block while waiting
-                *      for the lock to open...
-                */
+       /*
+        *      Try to lock fd.  If we can't, return.
+        *      If we can, continue.  This means that
+        *      the server doesn't block while waiting
+        *      for the lock to open...
+        */
        case STATE_UNLOCKED:
                /*
                 *      Note that we do NOT block waiting for
@@ -433,15 +457,20 @@ open_file:
                         *      try again.
                         */
                        close(data->work_fd);
+                       data->fp = NULL;
                        data->work_fd = -1;
                        data->state = STATE_UNOPENED;
                        return NULL;
                }
 
-               data->fp = fdopen(data->work_fd, "r");
+               /*
+                *      Only open for writing if we're
+                *      marking requests as completed.
+                */
+               data->fp = fdopen(data->work_fd, data->track ? "r+" : "r");
                if (!data->fp) {
-                       ERROR("FATAL: Failed to re-open detail file %s: %s",
-                              data->filename, fr_syserror(errno));
+                       ERROR("detail (%s): FATAL: Failed to re-open detail file: %s",
+                             data->name, fr_syserror(errno));
                        fr_exit(1);
                }
 
@@ -469,10 +498,8 @@ open_file:
                        struct stat buf;
 
                        if (fstat(data->work_fd, &buf) < 0) {
-                               ERROR("Failed to stat "
-                                      "detail file %s: %s",
-                                       data->filename,
-                                       fr_syserror(errno));
+                               ERROR("detail (%s): Failed to stat detail file: %s",
+                                     data->name, fr_syserror(errno));
 
                                goto cleanup;
                        }
@@ -487,8 +514,7 @@ open_file:
                 */
                if (feof(data->fp)) {
                cleanup:
-                       DEBUG("Detail - unlinking %s",
-                             data->filename_work);
+                       DEBUG("detail (%s): Unlinking %s", data->name, data->filename_work);
                        unlink(data->filename_work);
                        if (data->fp) fclose(data->fp);
                        data->fp = NULL;
@@ -497,7 +523,7 @@ open_file:
                        rad_assert(data->vps == NULL);
 
                        if (data->one_shot) {
-                               INFO("Finished reading \"one shot\" detail file - Exiting");
+                               INFO("detail (%s): Finished reading \"one shot\" detail file - Exiting", data->name);
                                radius_signal_self(RADIUS_SIGNAL_SELF_EXIT);
                        }
 
@@ -509,11 +535,11 @@ open_file:
                 */
                break;
 
-               /*
-                *      Read more value-pair's, unless we're
-                *      at EOF.  In that case, queue whatever
-                *      we have.
-                */
+       /*
+        *      Read more value-pair's, unless we're
+        *      at EOF.  In that case, queue whatever
+        *      we have.
+        */
        case STATE_READING:
                if (data->fp && !feof(data->fp)) break;
                data->state = STATE_QUEUED;
@@ -523,47 +549,54 @@ open_file:
        case STATE_QUEUED:
                goto alloc_packet;
 
-               /*
-                *      Periodically check what's going on.
-                *      If the request is taking too long,
-                *      retry it.
-                */
+       /*
+        *      Periodically check what's going on.
+        *      If the request is taking too long,
+        *      retry it.
+        */
        case STATE_RUNNING:
                if (time(NULL) < (data->running + (int)data->retry_interval)) {
                        return NULL;
                }
 
-               DEBUG("No response to detail request.  Retrying");
+               DEBUG("detail (%s): No response to detail request.  Retrying", data->name);
                /* FALL-THROUGH */
 
-               /*
-                *      If there's no reply, keep
-                *      retransmitting the current packet
-                *      forever.
-                */
+       /*
+        *      If there's no reply, keep
+        *      retransmitting the current packet
+        *      forever.
+        */
        case STATE_NO_REPLY:
                data->state = STATE_QUEUED;
                goto alloc_packet;
 
-               /*
-                *      We have a reply.  Clean up the old
-                *      request, and go read another one.
-                */
+       /*
+        *      We have a reply.  Clean up the old
+        *      request, and go read another one.
+        */
        case STATE_REPLIED:
                if (data->track) {
                        rad_assert(data->fp != NULL);
 
-                       if ((fseek(data->fp, data->timestamp_offset, SEEK_SET) < 0) ||
-                           (fwrite("\tDone", 1, 5, data->fp) < 5)) {
-                               WARN("Failed marking detail request as done: %s", fr_syserror(errno));
+                       if (fseek(data->fp, data->timestamp_offset, SEEK_SET) < 0) {
+                               WARN("detail (%s): Failed seeking to timestamp offset: %s",
+                                    data->name, fr_syserror(errno));
+                       } else if (fwrite("\tDone", 1, 5, data->fp) < 5) {
+                               WARN("detail (%s): Failed marking request as done: %s",
+                                    data->name, fr_syserror(errno));
+                       } else if (fflush(data->fp) != 0) {
+                               WARN("detail (%s): Failed flushing marked detail file to disk: %s",
+                                    data->name, fr_syserror(errno));
                        }
-                       fflush(data->fp);
+
                        if (fseek(data->fp, data->offset, SEEK_SET) < 0) {
-                               WARN("Failed seeking to next detail request: %s", fr_syserror(errno));
+                               WARN("detail (%s): Failed seeking to next detail request: %s",
+                                    data->name, fr_syserror(errno));
                        }
                }
 
-               pairfree(&data->vps);
+               fr_pair_list_free(&data->vps);
                data->state = STATE_HEADER;
                goto do_header;
        }
@@ -583,7 +616,7 @@ open_file:
                 *      FIXME: Maybe flag an error?
                 */
                if (!strchr(buffer, '\n')) {
-                       pairfree(&data->vps);
+                       fr_pair_list_free(&data->vps);
                        goto cleanup;
                }
 
@@ -618,8 +651,7 @@ open_file:
                 *      FIXME: print an error for badly formatted attributes?
                 */
                if (sscanf(buffer, "%255s %7s %1023s", key, op, value) != 3) {
-                       WARN("Skipping badly formatted line %s",
-                              buffer);
+                       WARN("detail (%s): Skipping badly formatted line %s", data->name, buffer);
                        continue;
                }
 
@@ -643,9 +675,9 @@ open_file:
                if (!strcasecmp(key, "Client-IP-Address")) {
                        data->client_ip.af = AF_INET;
                        if (ip_hton(&data->client_ip, AF_INET, value, false) < 0) {
-                               ERROR("Failed parsing Client-IP-Address");
+                               ERROR("detail (%s): Failed parsing Client-IP-Address", data->name);
 
-                               pairfree(&data->vps);
+                               fr_pair_list_free(&data->vps);
                                goto cleanup;
                        }
                        continue;
@@ -660,7 +692,7 @@ open_file:
                        data->timestamp = atoi(value);
                        data->timestamp_offset = data->last_offset;
 
-                       vp = paircreate(data, PW_PACKET_ORIGINAL_TIMESTAMP, 0);
+                       vp = fr_pair_afrom_num(data, PW_PACKET_ORIGINAL_TIMESTAMP, 0);
                        if (vp) {
                                vp->vp_date = (uint32_t) data->timestamp;
                                vp->type = VT_DATA;
@@ -682,7 +714,7 @@ open_file:
                 *      attributes like radsqlrelay does?
                 */
                vp = NULL;
-               if ((userparse(data, buffer, &vp) > 0) &&
+               if ((fr_pair_list_afrom_str(data, buffer, &vp) > 0) &&
                    (vp != NULL)) {
                        fr_cursor_merge(&cursor, vp);
                }
@@ -704,8 +736,8 @@ open_file:
         */
  alloc_packet:
        if (data->done_entry) {
-               DEBUG2("Skipping record for timestamp %lu", data->timestamp);
-               pairfree(&data->vps);
+               DEBUG2("detail (%s): Skipping record for timestamp %lu", data->name, data->timestamp);
+               fr_pair_list_free(&data->vps);
                data->state = STATE_HEADER;
                goto do_header;
        }
@@ -719,7 +751,9 @@ open_file:
         *      treat it as EOF.
         */
        if (data->state != STATE_QUEUED) {
-               ERROR("Truncated record: treating it as EOF for detail file %s", data->filename_work);
+               ERROR("detail (%s): Truncated record: treating it as EOF for detail file %s",
+                     data->name, data->filename_work);
+               fr_pair_list_free(&data->vps);
                goto cleanup;
        }
 
@@ -739,7 +773,7 @@ open_file:
         */
        packet = rad_alloc(NULL, true);
        if (!packet) {
-               ERROR("FATAL: Failed allocating memory for detail");
+               ERROR("detail (%s): FATAL: Failed allocating memory for detail", data->name);
                fr_exit(1);
        }
 
@@ -753,10 +787,10 @@ open_file:
         *      Otherwise, it lets us re-send the original packet
         *      contents, unmolested.
         */
-       packet->vps = paircopy(packet, data->vps);
+       packet->vps = fr_pair_list_copy(packet, data->vps);
 
        packet->code = PW_CODE_ACCOUNTING_REQUEST;
-       vp = pairfind(packet->vps, PW_PACKET_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_PACKET_TYPE, 0, TAG_ANY);
        if (vp) packet->code = vp->vp_integer;
 
        gettimeofday(&packet->timestamp, NULL);
@@ -769,13 +803,13 @@ open_file:
                packet->src_ipaddr = data->client_ip;
        }
 
-       vp = pairfind(packet->vps, PW_PACKET_SRC_IP_ADDRESS, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_PACKET_SRC_IP_ADDRESS, 0, TAG_ANY);
        if (vp) {
                packet->src_ipaddr.af = AF_INET;
                packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                packet->src_ipaddr.prefix = 32;
        } else {
-               vp = pairfind(packet->vps, PW_PACKET_SRC_IPV6_ADDRESS, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(packet->vps, PW_PACKET_SRC_IPV6_ADDRESS, 0, TAG_ANY);
                if (vp) {
                        packet->src_ipaddr.af = AF_INET6;
                        memcpy(&packet->src_ipaddr.ipaddr.ip6addr,
@@ -784,13 +818,13 @@ open_file:
                }
        }
 
-       vp = pairfind(packet->vps, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
        if (vp) {
                packet->dst_ipaddr.af = AF_INET;
                packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                packet->dst_ipaddr.prefix = 32;
        } else {
-               vp = pairfind(packet->vps, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(packet->vps, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY);
                if (vp) {
                        packet->dst_ipaddr.af = AF_INET6;
                        memcpy(&packet->dst_ipaddr.ipaddr.ip6addr,
@@ -819,7 +853,7 @@ open_file:
                 *      "Timestamp" field is when we wrote the packet to the
                 *      detail file, which could have been much later.
                 */
-               vp = pairfind(packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
                if (vp) {
                        data->timestamp = vp->vp_integer;
                }
@@ -828,11 +862,11 @@ open_file:
                 *      Look for Acct-Delay-Time, and update
                 *      based on Acct-Delay-Time += (time(NULL) - timestamp)
                 */
-               vp = pairfind(packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
                if (!vp) {
-                       vp = paircreate(packet, PW_ACCT_DELAY_TIME, 0);
+                       vp = fr_pair_afrom_num(packet, PW_ACCT_DELAY_TIME, 0);
                        rad_assert(vp != NULL);
-                       pairadd(&packet->vps, vp);
+                       fr_pair_add(&packet->vps, vp);
                }
                if (data->timestamp != 0) {
                        vp->vp_integer += time(NULL) - data->timestamp;
@@ -842,23 +876,14 @@ open_file:
        /*
         *      Set the transmission count.
         */
-       vp = pairfind(packet->vps, PW_PACKET_TRANSMIT_COUNTER, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_PACKET_TRANSMIT_COUNTER, 0, TAG_ANY);
        if (!vp) {
-               vp = paircreate(packet, PW_PACKET_TRANSMIT_COUNTER, 0);
+               vp = fr_pair_afrom_num(packet, PW_PACKET_TRANSMIT_COUNTER, 0);
                rad_assert(vp != NULL);
-               pairadd(&packet->vps, vp);
+               fr_pair_add(&packet->vps, vp);
        }
        vp->vp_integer = data->tries;
 
-       if (debug_flag) {
-               fr_printf_log("detail_recv: Read packet from %s\n", data->filename_work);
-               for (vp = fr_cursor_init(&cursor, &packet->vps);
-                    vp;
-                    vp = fr_cursor_next(&cursor)) {
-                       debug_pair(vp);
-               }
-       }
-
        data->state = STATE_RUNNING;
        data->running = packet->timestamp.tv_sec;
 
@@ -885,7 +910,7 @@ void detail_free(rad_listen_t *this)
                data->child_pipe[0] = -1;
 
                /*
-                *      Tell it to stop (interrupting it's sleep)
+                *      Tell it to stop (interrupting its sleep)
                 */
                pthread_kill(data->pthread_id, SIGTERM);
 
@@ -894,10 +919,12 @@ void detail_free(rad_listen_t *this)
                 */
                ret = read(data->master_pipe[0], &arg, sizeof(arg));
                if (ret < 0) {
-                       ERROR("Reader thread exited without informing the master: %s", fr_syserror(errno));
+                       ERROR("detail (%s): Reader thread exited without informing the master: %s",
+                             data->name, 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);
+                       ERROR("detail (%s): Invalid thread pointer received from reader thread during exit",
+                             data->name);
+                       ERROR("detail (%s): Expected %zu bytes, got %zi bytes", data->name, sizeof(arg), ret);
                }
 
                close(data->master_pipe[0]);
@@ -940,8 +967,8 @@ static int detail_delay(listen_detail_t *data)
        delay += (USEC * 3) / 4;
        delay += fr_rand() % (USEC / 2);
 
-       DEBUG2("Detail listener %s state %s waiting %d.%06d sec",
-              data->filename,
+       DEBUG2("detail (%s): Detail listener state %s waiting %d.%06d sec",
+              data->name,
               fr_int2str(state_names, data->state, "?"),
               (delay / USEC), delay % USEC);
 
@@ -965,8 +992,9 @@ int detail_encode(UNUSED rad_listen_t *this, UNUSED REQUEST *request)
 
        data->signal = 0;
 
-       DEBUG2("Detail listener %s state %s signalled %d waiting %d.%06d sec",
-              data->filename, fr_int2str(state_names, data->state, "?"),
+       DEBUG2("detail (%s): Detail listener state %s signalled %d waiting %d.%06d sec",
+              data->name,
+              fr_int2str(state_names, data->state, "?"),
               data->signal,
               data->delay_time / USEC,
               data->delay_time % USEC);
@@ -1010,7 +1038,8 @@ static void *detail_handler_thread(void *arg)
                        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));
+                                       ERROR("detail (%s): Failed writing exit status to master: %s",
+                                             data->name, fr_syserror(errno));
                                }
                                return NULL;
                        }
@@ -1023,11 +1052,13 @@ static void *detail_handler_thread(void *arg)
                 */
                do {
                        if (write(data->master_pipe[1], &packet, sizeof(packet)) < 0) {
-                               ERROR("Failed passing detail packet pointer to master: %s", fr_syserror(errno));
+                               ERROR("detail (%s): Failed passing detail packet pointer to master: %s",
+                                     data->name, fr_syserror(errno));
                        }
 
                        if (read(data->child_pipe[0], &c, 1) < 0) {
-                               ERROR("Failed getting detail packet ack from master: %s", fr_syserror(errno));
+                               ERROR("detail (%s): Failed getting detail packet ack from master: %s",
+                                     data->name, fr_syserror(errno));
                                break;
                        }
 
@@ -1049,11 +1080,9 @@ static const CONF_PARSER detail_config[] = {
        { "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 },
-       { "track", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, listen_detail_t, track), NULL },
-       { "max_outstanding", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_detail_t, load_factor), NULL },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       { "one_shot", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, listen_detail_t, one_shot), "no" },
+       { "track", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, listen_detail_t, track), "no" },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -1064,7 +1093,7 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
        int             rcode;
        listen_detail_t *data;
        RADCLIENT       *client;
-       char buffer[2048];
+       char            buffer[2048];
 
        data = this->data;
 
@@ -1074,6 +1103,9 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
                return -1;
        }
 
+       data->name = cf_section_name2(cs);
+       if (!data->name) data->name = data->filename;
+
        /*
         *      We don't do duplicate detection for "detail" sockets.
         */
@@ -1085,24 +1117,21 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
                return -1;
        }
 
-       if ((data->load_factor < 1) || (data->load_factor > 100)) {
-               cf_log_err_cs(cs, "Load factor must be between 1 and 100");
-               return -1;
-       }
+       FR_INTEGER_BOUND_CHECK("load_factor", data->load_factor, >=, 1);
+       FR_INTEGER_BOUND_CHECK("load_factor", data->load_factor, <=, 100);
 
-       if ((data->poll_interval < 1) || (data->poll_interval > 20)) {
-               cf_log_err_cs(cs, "poll_interval must be between 1 and 20");
-               return -1;
-       }
-
-       if (check_config) return 0;
-
-       if (data->max_outstanding == 0) data->max_outstanding = 1;
+       FR_INTEGER_BOUND_CHECK("poll_interval", data->poll_interval, >=, 1);
+       FR_INTEGER_BOUND_CHECK("poll_interval", data->poll_interval, <=, 60);
 
        FR_INTEGER_BOUND_CHECK("retry_interval", data->retry_interval, >=, 4);
        FR_INTEGER_BOUND_CHECK("retry_interval", data->retry_interval, <=, 3600);
 
        /*
+        *      Only checking the config.  Don't start threads or anything else.
+        */
+       if (check_config) return 0;
+
+       /*
         *      If the filename is a glob, use "detail.work" as the
         *      work file name.
         */
@@ -1111,8 +1140,8 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
                char *p;
 
 #ifndef HAVE_GLOB_H
-               WARN("Detail file \"%s\" appears to use file globbing, but it is not supported on this system.",
-                    data->filename);
+               WARN("detail (%s): File \"%s\" appears to use file globbing, but it is not supported on this system",
+                    data->name, data->filename);
 #endif
                strlcpy(buffer, data->filename, sizeof(buffer));
                p = strrchr(buffer, FR_DIR_SEP);
@@ -1121,6 +1150,16 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
                } else {
                        buffer[0] = '\0';
                }
+
+               /*
+                *      Globbing cannot be done across directories.
+                */
+               if ((strchr(buffer, '*') != NULL) ||
+                   (strchr(buffer, '[') != NULL)) {
+                       cf_log_err_cs(cs, "Wildcard directories are not supported");
+                       return -1;
+               }
+
                strlcat(buffer, "detail.work",
                        sizeof(buffer) - strlen(buffer));
 
@@ -1154,14 +1193,12 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
         *      Create the communication pipes.
         */
        if (pipe(data->master_pipe) < 0) {
-               ERROR("radiusd: Error opening internal pipe: %s",
-                     fr_syserror(errno));
+               ERROR("detail (%s): Error opening internal pipe: %s", data->name, fr_syserror(errno));
                fr_exit(1);
        }
 
        if (pipe(data->child_pipe) < 0) {
-               ERROR("radiusd: Error opening internal pipe: %s",
-                     fr_syserror(errno));
+               ERROR("detail (%s): Error opening internal pipe: %s", data->name, fr_syserror(errno));
                fr_exit(1);
        }
 
index 48595f5..bd49e8b 100644 (file)
@@ -68,7 +68,7 @@ static bool all_digits(char const *string)
 
 /** Evaluate a template
  *
- * Converts a value_pair_tmpl_t to a boolean value.
+ * Converts a vp_tmpl_t to a boolean value.
  *
  * @param[in] request the REQUEST
  * @param[in] modreturn the previous module return code
@@ -76,7 +76,7 @@ static bool all_digits(char const *string)
  * @param[in] vpt the template to evaluate
  * @return -1 on error, 0 for "no match", 1 for "match".
  */
-int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, value_pair_tmpl_t const *vpt)
+int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth, vp_tmpl_t const *vpt)
 {
        int rcode;
        int modcode;
@@ -155,7 +155,7 @@ static int cond_do_regex(REQUEST *request, fr_cond_t const *c,
                         PW_TYPE lhs_type, value_data_t const *lhs, size_t lhs_len,
                         PW_TYPE rhs_type, value_data_t const *rhs, size_t rhs_len)
 {
-       value_pair_map_t const *map = c->data.map;
+       vp_map_t const *map = c->data.map;
 
        ssize_t         slen;
        int             ret;
@@ -280,7 +280,7 @@ static int cond_cmp_values(REQUEST *request, fr_cond_t const *c,
                           PW_TYPE lhs_type, value_data_t const *lhs, size_t lhs_len,
                           PW_TYPE rhs_type, value_data_t const *rhs, size_t rhs_len)
 {
-       value_pair_map_t const *map = c->data.map;
+       vp_map_t const *map = c->data.map;
        int rcode;
 
 #ifdef WITH_EVAL_DEBUG
@@ -306,7 +306,7 @@ static int cond_cmp_values(REQUEST *request, fr_cond_t const *c,
                EVAL_DEBUG("CMP WITH PAIRCOMPARE");
                rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
 
-               vp = pairalloc(request, map->lhs->tmpl_da);
+               vp = fr_pair_afrom_da(request, map->lhs->tmpl_da);
                vp->op = c->data.map->op;
 
                value_data_copy(vp, &vp->data, rhs_type, rhs, rhs_len);
@@ -353,10 +353,11 @@ finish:
  *
  * @return -1 on error, 0 for "no match", 1 for "match".
  */
-static int cond_normalise_values(REQUEST *request, fr_cond_t const *c,
-                                PW_TYPE lhs_type, DICT_ATTR const *lhs_enumv, value_data_t const *lhs, size_t lhs_len)
+static int cond_normalise_and_cmp(REQUEST *request, fr_cond_t const *c,
+                                 PW_TYPE lhs_type, DICT_ATTR const *lhs_enumv,
+                                 value_data_t const *lhs, size_t lhs_len)
 {
-       value_pair_map_t const *map = c->data.map;
+       vp_map_t const *map = c->data.map;
 
        DICT_ATTR const *cast = NULL;
        PW_TYPE cast_type = PW_TYPE_INVALID;
@@ -585,7 +586,7 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
 {
        int rcode = 0;
 
-       value_pair_map_t const *map = c->data.map;
+       vp_map_t const *map = c->data.map;
 
        EVAL_DEBUG(">>> MAP TYPES LHS: %s, RHS: %s",
                   fr_int2str(tmpl_names, map->lhs->type, "???"),
@@ -608,7 +609,7 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
 #ifndef NDEBUG
                        rad_assert(radius_find_compare(map->lhs->tmpl_da)); /* expensive assert */
 #endif
-                       rcode = cond_normalise_values(request, c, PW_TYPE_INVALID, NULL, NULL, 0);
+                       rcode = cond_normalise_and_cmp(request, c, PW_TYPE_INVALID, NULL, NULL, 0);
                        break;
                }
                for (vp = tmpl_cursor_init(&rcode, &cursor, request, map->lhs);
@@ -619,14 +620,14 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                         *      if we get at least one set of operands that
                         *      evaluates to true.
                         */
-                       rcode = cond_normalise_values(request, c, vp->da->type, vp->da, &vp->data, vp->vp_length);
+                       rcode = cond_normalise_and_cmp(request, c, vp->da->type, vp->da, &vp->data, vp->vp_length);
                        if (rcode != 0) break;
                }
        }
                break;
 
        case TMPL_TYPE_DATA:
-               rcode = cond_normalise_values(request, c,
+               rcode = cond_normalise_and_cmp(request, c,
                                              map->lhs->tmpl_data_type, NULL, &map->lhs->tmpl_data_value,
                                              map->lhs->tmpl_data_length);
                break;
@@ -654,7 +655,7 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                }
                rad_assert(data.strvalue);
 
-               rcode = cond_normalise_values(request, c, PW_TYPE_STRING, NULL, &data, ret);
+               rcode = cond_normalise_and_cmp(request, c, PW_TYPE_STRING, NULL, &data, ret);
                if (map->lhs->type != TMPL_TYPE_LITERAL) talloc_free(data.ptr);
        }
                break;
@@ -751,11 +752,11 @@ int radius_evaluate_cond(REQUEST *request, int modreturn, int depth, fr_cond_t c
 
 
 /*
- *     The pairmove() function in src/lib/valuepair.c does all sorts of
+ *     The fr_pair_list_move() function in src/lib/valuepair.c does all sorts of
  *     extra magic that we don't want here.
  *
  *     FIXME: integrate this with the code calling it, so that we
- *     only paircopy() those attributes that we're really going to
+ *     only fr_pair_list_copy() those attributes that we're really going to
  *     use.
  */
 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat)
@@ -764,10 +765,11 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
        vp_cursor_t cursor;
        VALUE_PAIR *vp, *next, **last;
        VALUE_PAIR **from_list, **to_list;
+       VALUE_PAIR *append, **append_tail;
+       VALUE_PAIR *to_copy;
        bool *edited = NULL;
        REQUEST *fixup = NULL;
-
-       if (!request) return;
+       TALLOC_CTX *ctx;
 
        /*
         *      Set up arrays for editing, to remove some of the
@@ -776,7 +778,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
         *
         *      It also means that the operators apply ONLY to the
         *      attributes in the original list.  With the previous
-        *      implementation of pairmove(), adding two attributes
+        *      implementation of fr_pair_list_move(), adding two attributes
         *      via "+=" and then "=" would mean that the second one
         *      wasn't added, because of the existence of the first
         *      one in the "to" list.  This implementation doesn't
@@ -785,7 +787,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
         *      Also, the previous implementation did NOT implement
         *      "-=" correctly.  If two of the same attributes existed
         *      in the "to" list, and you tried to subtract something
-        *      matching the *second* value, then the pairdelete()
+        *      matching the *second* value, then the fr_pair_delete_by_num()
         *      function was called, and the *all* attributes of that
         *      number were deleted.  With this implementation, only
         *      the matching attributes are deleted.
@@ -797,6 +799,9 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
        for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++;
        to_list = talloc_array(request, VALUE_PAIR *, count);
 
+       append = NULL;
+       append_tail = &append;
+
        /*
         *      Move the lists to the arrays, and break the list
         *      chains.
@@ -809,7 +814,9 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
        }
 
        to_count = 0;
-       for (vp = *to; vp != NULL; vp = next) {
+       ctx = talloc_parent(*to);
+       to_copy = fr_pair_list_copy(ctx, *to);
+       for (vp = to_copy; vp != NULL; vp = next) {
                next = vp->next;
                to_list[to_count++] = vp;
                vp->next = NULL;
@@ -835,7 +842,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                 *      is empty, and we're supposed to replace or
                 *      "add if not existing".
                 */
-               if (from_list[i]->op == T_OP_ADD) goto append;
+               if (from_list[i]->op == T_OP_ADD) goto do_append;
 
                found = false;
                for (j = 0; j < to_count; j++) {
@@ -863,7 +870,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                        if (from_list[i]->op == T_OP_SET) {
                                RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
                                       to_list[j]->da->name, i, j);
-                               pairfree(&to_list[j]);
+                               fr_pair_list_free(&to_list[j]);
                                to_list[j] = from_list[i];
                                from_list[i] = NULL;
                                edited[j] = true;
@@ -928,7 +935,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                                        delete:
                                                RDEBUG4("::: DELETING %s FROM %d TO %d",
                                                       from_list[i]->da->name, i, j);
-                                               pairfree(&to_list[j]);
+                                               fr_pair_list_free(&to_list[j]);
                                                to_list[j] = NULL;
                                        }
                                        break;
@@ -941,7 +948,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                                        if (rcode > 0) {
                                                RDEBUG4("::: REPLACING %s FROM %d TO %d",
                                                       from_list[i]->da->name, i, j);
-                                               pairfree(&to_list[j]);
+                                               fr_pair_list_free(&to_list[j]);
                                                to_list[j] = from_list[i];
                                                from_list[i] = NULL;
                                                edited[j] = true;
@@ -952,7 +959,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                                        if (rcode < 0) {
                                                RDEBUG4("::: REPLACING %s FROM %d TO %d",
                                                       from_list[i]->da->name, i, j);
-                                               pairfree(&to_list[j]);
+                                               fr_pair_list_free(&to_list[j]);
                                                to_list[j] = from_list[i];
                                                from_list[i] = NULL;
                                                edited[j] = true;
@@ -977,11 +984,13 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                            (from_list[i]->op == T_OP_LE) ||
                            (from_list[i]->op == T_OP_GE) ||
                            (from_list[i]->op == T_OP_SET)) {
-                       append:
+                       do_append:
                                RDEBUG4("::: APPENDING %s FROM %d TO %d",
                                       from_list[i]->da->name, i, tailto);
-                               to_list[tailto++] = from_list[i];
+                               *append_tail = from_list[i];
+                               from_list[i]->op = T_OP_EQ;
                                from_list[i] = NULL;
+                               append_tail = &(*append_tail)->next;
                        }
                }
        }
@@ -992,7 +1001,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
        for (i = 0; i < from_count; i++) {
                if (!from_list[i]) continue;
 
-               pairfree(&from_list[i]);
+               fr_pair_list_free(&from_list[i]);
        }
        talloc_free(from_list);
 
@@ -1001,7 +1010,7 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
        /*
         *      Re-chain the "to" list.
         */
-       *to = NULL;
+       fr_pair_list_free(to);
        last = to;
 
        if (to == &request->packet->vps) {
@@ -1009,10 +1018,6 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
        } else if (request->parent && (to == &request->parent->packet->vps)) {
                fixup = request->parent;
        }
-       if (fixup) {
-               fixup->username = NULL;
-               fixup->password = NULL;
-       }
 
        for (i = 0; i < tailto; i++) {
                if (!to_list[i]) continue;
@@ -1029,12 +1034,27 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                 */
                vp->op = T_OP_EQ;
 
-               /*
-                *      Fix dumb cache issues
-                */
-               if (fixup && !vp->da->vendor) {
-                       if ((vp->da->attr == PW_USER_NAME) &&
-                           !fixup->username) {
+               *last = vp;
+               last = &(*last)->next;
+       }
+
+       /*
+        *      And finally add in the attributes we're appending to
+        *      the tail of the "to" list.
+        */
+       *last = append;
+
+       /*
+        *      Fix dumb cache issues
+        */
+       if (fixup) {
+               fixup->username = NULL;
+               fixup->password = NULL;
+
+               for (vp = fixup->packet->vps; vp != NULL; vp = vp->next) {
+                       if (vp->da->vendor) continue;
+
+                       if ((vp->da->attr == PW_USER_NAME) && !fixup->username) {
                                fixup->username = vp;
 
                        } else if (vp->da->attr == PW_STRIPPED_USER_NAME) {
@@ -1044,9 +1064,6 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool d
                                fixup->password = vp;
                        }
                }
-
-               *last = vp;
-               last = &(*last)->next;
        }
 
        rad_assert(request->packet != NULL);
index 48ff6ea..d8a73c3 100644 (file)
@@ -93,15 +93,23 @@ pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait,
        int from_child[2] = {-1, -1};
        pid_t pid;
 #endif
-       int argc;
-       int i;
-       char *argv[MAX_ARGV];
-       char argv_buf[4096];
+       int             argc;
+       int             i;
+       char const      **argv_p;
+       char            *argv[MAX_ARGV], **argv_start = argv;
+       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);
+       /*
+        *      Stupid array decomposition...
+        *
+        *      If we do memcpy(&argv_p, &argv, sizeof(argv_p)) src ends up being a char **
+        *      pointing to the value of the first element.
+        */
+       memcpy(&argv_p, &argv_start, sizeof(argv_p));
+       argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv_p, true, sizeof(argv_buf), argv_buf);
        if (argc <= 0) {
                DEBUG("invalid command line '%s'.", cmd);
                return -1;
@@ -109,7 +117,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait,
 
 
 #ifndef NDEBUG
-       if (debug_flag > 2) {
+       if (rad_debug_lvl > 2) {
                DEBUG3("executing cmd %s", cmd);
                for (i = 0; i < argc; i++) {
                        DEBUG3("\t[%d] %s", i, argv[i]);
@@ -251,7 +259,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait,
                 *      If we are debugging, then we want the error
                 *      messages to go to the STDERR of the server.
                 */
-               if (debug_flag == 0) {
+               if (rad_debug_lvl == 0) {
                        dup2(devnull, STDERR_FILENO);
                }
                close(devnull);
@@ -499,6 +507,7 @@ int radius_readfrom_program(int fd, pid_t pid, int timeout,
 
 /** Execute a program.
  *
+ * @param[in,out] ctx to allocate new VALUE_PAIR (s) in.
  * @param[out] out buffer to append plaintext (non valuepair) output.
  * @param[in] outlen length of out buffer.
  * @param[out] output_pairs list of value pairs - child stdout will be parsed and added into this list
@@ -513,7 +522,7 @@ int radius_readfrom_program(int fd, pid_t pid, int timeout,
 
  * @return 0 if exec_wait==0, exit code if exec_wait!=0, -1 on error.
  */
-int radius_exec_program(char *out, size_t outlen, VALUE_PAIR **output_pairs,
+int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, VALUE_PAIR **output_pairs,
                        REQUEST *request, char const *cmd, VALUE_PAIR *input_pairs,
                        bool exec_wait, bool shell_escape, int timeout)
 
@@ -571,9 +580,9 @@ int radius_exec_program(char *out, size_t outlen, VALUE_PAIR **output_pairs,
        if (output_pairs) {
                /*
                 *      HACK: Replace '\n' with ',' so that
-                *      userparse() can parse the buffer in
+                *      fr_pair_list_afrom_str() can parse the buffer in
                 *      one go (the proper way would be to
-                *      fix userparse(), but oh well).
+                *      fix fr_pair_list_afrom_str(), but oh well).
                 */
                for (p = answer; *p; p++) {
                        if (*p == '\n') {
@@ -593,7 +602,7 @@ int radius_exec_program(char *out, size_t outlen, VALUE_PAIR **output_pairs,
                        answer[--len] = '\0';
                }
 
-               if (userparse(request, answer, output_pairs) == T_INVALID) {
+               if (fr_pair_list_afrom_str(ctx, answer, output_pairs) == T_INVALID) {
                        RERROR("Failed parsing output from: %s: %s", cmd, fr_strerror());
                        strlcpy(out, answer, len);
                        ret = -1;
index d8a1acb..5388ca0 100644 (file)
@@ -46,6 +46,7 @@ struct exfile_t {
        pthread_mutex_t mutex;
 #endif
        exfile_entry_t *entries;
+       bool            locking;
 };
 
 
@@ -61,6 +62,9 @@ struct exfile_t {
 #define PTHREAD_MUTEX_UNLOCK(_x)
 #endif
 
+#define MAX_TRY_LOCK 4                 //!< How many times we attempt to acquire a lock
+                                       //!< before giving up.
+
 static int _exfile_free(exfile_t *ef)
 {
        uint32_t i;
@@ -88,9 +92,10 @@ static int _exfile_free(exfile_t *ef)
  * @param ctx The talloc context
  * @param max_entries Max file descriptors to cache, and manage locks for.
  * @param max_idle Maximum time a file descriptor can be idle before it's closed.
+ * @param locking whether or not to lock the files.
  * @return the new context, or NULL on error.
  */
-exfile_t *exfile_init(TALLOC_CTX *ctx, uint32_t max_entries, uint32_t max_idle)
+exfile_t *exfile_init(TALLOC_CTX *ctx, uint32_t max_entries, uint32_t max_idle, bool locking)
 {
        exfile_t *ef;
 
@@ -112,6 +117,7 @@ exfile_t *exfile_init(TALLOC_CTX *ctx, uint32_t max_entries, uint32_t max_idle)
 
        ef->max_entries = max_entries;
        ef->max_idle = max_idle;
+       ef->locking = locking;
 
        talloc_set_destructor(ef, _exfile_free);
 
@@ -131,7 +137,7 @@ exfile_t *exfile_init(TALLOC_CTX *ctx, uint32_t max_entries, uint32_t max_idle)
  */
 int exfile_open(exfile_t *ef, char const *filename, mode_t permissions, bool append)
 {
-       uint32_t i;
+       uint32_t i, tries;
        uint32_t hash;
        time_t now = time(NULL);
        struct stat st;
@@ -263,9 +269,34 @@ do_return:
                return -1;
        }
 
-       if (rad_lockfd(ef->entries[i].fd, 0) < 0) {
-               fr_strerror_printf("Failed to lock file %s: %s", filename, strerror(errno));
-               goto error;
+       /*
+        *      Try to lock it.  If we can't lock it, it's because
+        *      some reader has re-named the file to "foo.work" and
+        *      locked it.  So, we close the current file, re-open it,
+        *      and try again/
+        */
+       if (ef->locking) {
+               for (tries = 0; tries < MAX_TRY_LOCK; tries++) {
+                       if (rad_lockfd_nonblock(ef->entries[i].fd, 0) >= 0) break;
+
+                       if (errno != EAGAIN) {
+                               fr_strerror_printf("Failed to lock file %s: %s", filename, strerror(errno));
+                               goto error;
+                       }
+
+                       close(ef->entries[i].fd);
+                       ef->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
+                       if (ef->entries[i].fd < 0) {
+                               fr_strerror_printf("Failed to open file %s: %s",
+                                                  filename, strerror(errno));
+                               goto error;
+                       }
+               }
+
+               if (tries >= MAX_TRY_LOCK) {
+                       fr_strerror_printf("Failed to lock file %s: too many tries", filename);
+                       goto error;
+               }
        }
 
        /*
@@ -327,7 +358,7 @@ int exfile_close(exfile_t *ef, int fd)
                 *      Unlock the bytes that we had previously locked.
                 */
                if (ef->entries[i].dup == fd) {
-                       (void) rad_unlockfd(ef->entries[i].dup, 0);
+                       if (ef->locking) (void) rad_unlockfd(ef->entries[i].dup, 0);
                        close(ef->entries[i].dup); /* releases the fcntl lock */
                        ef->entries[i].dup = -1;
 
index fb80df3..d6a8c92 100644 (file)
@@ -89,7 +89,7 @@ int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int compl
        PAIR_LIST *pl = NULL, *t;
        PAIR_LIST **last = &pl;
        int lineno = 0;
-       int old_lineno = 0;
+       int entry_lineno = 0;
        FR_TOKEN parsecode;
 #ifdef HAVE_REGEX_H
        VALUE_PAIR *vp;
@@ -111,8 +111,6 @@ int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int compl
                return -1;
        }
 
-       parsecode = T_EOL;
-
        /*
         *      Read the entire file into memory for speed.
         */
@@ -152,7 +150,7 @@ parse_again:
                         */                   
                        ptr = buffer;
                        getword(&ptr, entry, sizeof(entry), false);
-                       old_lineno = lineno;
+                       entry_lineno = lineno;
 
                        /*
                         *      Include another file if we see
@@ -211,7 +209,7 @@ parse_again:
                         */
                        rad_assert(check_tmp == NULL);
                        rad_assert(reply_tmp == NULL);
-                       parsecode = userparse(ctx, ptr, &check_tmp);
+                       parsecode = fr_pair_list_afrom_str(ctx, ptr, &check_tmp);
                        if (parsecode == T_INVALID) {
                                pairlist_free(&pl);
                                ERROR("%s[%d]: Parse error (check) for entry %s: %s",
@@ -279,7 +277,7 @@ parse_again:
                        talloc_free(check_tmp);
                        talloc_free(reply_tmp);
                        ERROR("%s[%d]: Invalid comma after the reply attributes.  Please delete it.",
-                             file, old_lineno);
+                             file, lineno);
                        fclose(fp);
                        return -1;
                }
@@ -288,9 +286,8 @@ parse_again:
                 *      Parse the reply values.  If there's a trailing
                 *      comma, keep parsing the reply values.
                 */
-               parsecode = userparse(ctx, buffer, &reply_tmp);
+               parsecode = fr_pair_list_afrom_str(ctx, buffer, &reply_tmp);
                if (parsecode == T_COMMA) {
-                       old_lineno = lineno;
                        continue;
                }
 
@@ -313,12 +310,12 @@ parse_again:
                 */
                MEM(t = talloc_zero(ctx, PAIR_LIST));
 
-               if (check_tmp) pairsteal(t, check_tmp);
-               if (reply_tmp) pairsteal(t, reply_tmp);
+               if (check_tmp) fr_pair_steal(t, check_tmp);
+               if (reply_tmp) fr_pair_steal(t, reply_tmp);
 
                t->check = check_tmp;
                t->reply = reply_tmp;
-               t->lineno = old_lineno;
+               t->lineno = entry_lineno;
                check_tmp = NULL;
                reply_tmp = NULL;
 
index 1ac1bc9..8ddc811 100644 (file)
@@ -48,6 +48,9 @@ RCSID("$Id$")
 #include <fcntl.h>
 #endif
 
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
 
 #ifdef DEBUG_PRINT_PACKET
 static void print_packet(RADIUS_PACKET *packet)
@@ -75,36 +78,6 @@ static int command_write_magic(int newfd, listen_socket_t *sock);
 static fr_protocol_t master_listen[];
 
 /*
- *     Xlat for %{listen:foo}
- */
-static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
-                          char const *fmt, char *out,
-                          size_t outlen)
-{
-       char const *value = NULL;
-       CONF_PAIR *cp;
-
-       if (!fmt || !out || (outlen < 1)) return 0;
-
-       if (!request->listener) {
-               RWDEBUG("No listener associated with this request");
-               *out = '\0';
-               return 0;
-       }
-
-       cp = cf_pair_find(request->listener->cs, fmt);
-       if (!cp || !(value = cf_pair_value(cp))) {
-               RDEBUG("Listener does not contain config item \"%s\"", fmt);
-               *out = '\0';
-               return 0;
-       }
-
-       strlcpy(out, value, outlen);
-
-       return strlen(out);
-}
-
-/*
  *     Find a per-socket client.
  */
 RADCLIENT *client_listener_find(rad_listen_t *listener,
@@ -144,7 +117,7 @@ RADCLIENT *client_listener_find(rad_listen_t *listener,
                 *      If they're running in debug mode, show them
                 *      every packet.
                 */
-               if (debug_flag == 0) {
+               if (rad_debug_lvl == 0) {
                        static time_t last_printed = 0;
 
                        now = time(NULL);
@@ -183,6 +156,10 @@ RADCLIENT *client_listener_find(rad_listen_t *listener,
         *      It's a dynamically generated client, check it.
         */
        if (client->dynamic && (src_port != 0)) {
+#ifdef HAVE_SYS_STAT_H
+               char const *filename;
+#endif
+
                /*
                 *      Lives forever.  Return it.
                 */
@@ -201,6 +178,26 @@ RADCLIENT *client_listener_find(rad_listen_t *listener,
                 */
                if ((client->created + client->lifetime) > now) return client;
 
+#ifdef HAVE_SYS_STAT_H
+               /*
+                *      The client was read from a file, and the file
+                *      hasn't changed since the client was created.
+                *      Just renew the creation time, and continue.
+                *      We don't need to re-load the same information.
+                */
+               if (client->cs &&
+                   (filename = cf_section_filename(client->cs)) != NULL) {
+                       struct stat buf;
+
+                       if ((stat(filename, &buf) >= 0) &&
+                           (buf.st_mtime < client->created)) {
+                               client->created = now;
+                               return client;
+                       }
+               }
+#endif
+
+
                /*
                 *      This really puts them onto a queue for later
                 *      deletion.
@@ -245,6 +242,7 @@ RADCLIENT *client_listener_find(rad_listen_t *listener,
        request->packet = rad_recv(NULL, listener->fd, 0x02); /* MSG_PEEK */
        if (!request->packet) {                         /* badly formed, etc */
                talloc_free(request);
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                goto unknown;
        }
        (void) talloc_steal(request, request->packet);
@@ -551,6 +549,7 @@ static int dual_tcp_recv(rad_listen_t *listener)
        return 1;
 }
 
+
 static int dual_tcp_accept(rad_listen_t *listener)
 {
        int newfd;
@@ -642,9 +641,11 @@ static int dual_tcp_accept(rad_listen_t *listener)
        sock->limit.num_connections++;
 
        /*
-        *      Add the new listener.
+        *      Add the new listener.  We require a new context here,
+        *      because the allocations for the packet, etc. in the
+        *      child listener will be done in a child thread.
         */
-       this = listen_alloc(listener, listener->type);
+       this = listen_alloc(NULL, listener->type);
        if (!this) return -1;
 
        /*
@@ -685,6 +686,10 @@ static int dual_tcp_accept(rad_listen_t *listener)
        this->fd = newfd;
        this->status = RAD_LISTEN_STATUS_INIT;
 
+       this->parent = listener;
+       if (!rbtree_insert(listener->children, this)) {
+               ERROR("Failed inserting TCP socket into parent list.");
+       }
 
 #ifdef WITH_COMMAND_SOCKET
        if (this->type == RAD_LISTEN_COMMAND) {
@@ -885,8 +890,7 @@ static CONF_PARSER performance_config[] = {
        { "synchronous", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rad_listen_t, synchronous), NULL },
 
        { "workers", FR_CONF_OFFSET(PW_TYPE_INTEGER, rad_listen_t, workers), NULL },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -898,10 +902,35 @@ static CONF_PARSER limit_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
+
+#ifdef WITH_TCP
+/*
+ *     TLS requires child threads to handle the listeners.  Which
+ *     means that we need a separate talloc context per child thread.
+ *     Which means that we need to manually clean up the child
+ *     listeners.  Which means we need to manually track them.
+ *
+ *     All child thread linking/unlinking is done in the master
+ *     thread.  If we care, we can later add a mutex for the parent
+ *     listener.
+ */
+static int listener_cmp(void const *one, void const *two)
+{
+       if (one < two) return -1;
+       if (one > two) return +1;
+       return 0;
+}
+
+static int listener_unlink(UNUSED void *ctx, UNUSED void *data)
+{
+       return 2;               /* unlink this node from the tree */
+}
+#endif
+
+
 /*
  *     Parse an authentication or accounting socket.
  */
@@ -954,7 +983,7 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                rcode = cf_item_parse(cs, "proto", FR_ITEM_POINTER(PW_TYPE_STRING, &proto), "udp");
                if (rcode < 0) return -1;
 
-               if (strcmp(proto, "udp") == 0) {
+               if (!proto || strcmp(proto, "udp") == 0) {
                        sock->proto = IPPROTO_UDP;
 
                } else if (strcmp(proto, "tcp") == 0) {
@@ -1235,9 +1264,9 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        if (!client_cs) client_cs = parentcs;
 
 #ifdef WITH_TLS
-       sock->clients = clients_parse_section(client_cs, (this->tls != NULL));
+       sock->clients = client_list_parse_section(client_cs, (this->tls != NULL));
 #else
-       sock->clients = clients_parse_section(client_cs, false);
+       sock->clients = client_list_parse_section(client_cs, false);
 #endif
        if (!sock->clients) {
                cf_log_err_cs(cs,
@@ -1252,6 +1281,12 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                 *      allow us to accept the socket.
                 */
                this->recv = dual_tcp_accept;
+
+               this->children = rbtree_create(this, listener_cmp, NULL, 0);
+               if (!this->children) {
+                       cf_log_err_cs(cs, "Failed to create child list for TCP socket.");
+                       return -1;
+               }
        }
 #endif
 
@@ -1375,6 +1410,7 @@ static int stats_socket_recv(rad_listen_t *listener)
        FR_STATS_INC(auth, total_requests);
 
        if (rcode < 20) {       /* RADIUS_HDR_LEN */
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                FR_STATS_INC(auth, total_malformed_requests);
                return 0;
        }
@@ -1406,7 +1442,7 @@ static int stats_socket_recv(rad_listen_t *listener)
        packet = rad_recv(NULL, listener->fd, 1); /* require message authenticator */
        if (!packet) {
                FR_STATS_INC(auth, total_malformed_requests);
-               DEBUG("%s", fr_strerror());
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                return 0;
        }
 
@@ -1444,6 +1480,7 @@ static int auth_socket_recv(rad_listen_t *listener)
        FR_STATS_INC(auth, total_requests);
 
        if (rcode < 20) {       /* RADIUS_HDR_LEN */
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                FR_STATS_INC(auth, total_malformed_requests);
                return 0;
        }
@@ -1479,8 +1516,8 @@ static int auth_socket_recv(rad_listen_t *listener)
                rad_recv_discard(listener->fd);
                FR_STATS_INC(auth, total_unknown_types);
 
-               DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED",
-                     code, client->shortname, src_port);
+               if (DEBUG_ENABLED) ERROR("Receive - Invalid packet code %d sent to authentication port from "
+                                        "client %s port %d", code, client->shortname, src_port);
                return 0;
        } /* switch over packet types */
 
@@ -1490,6 +1527,7 @@ static int auth_socket_recv(rad_listen_t *listener)
                FR_STATS_INC(auth, total_packets_dropped);
                return 0;
        }
+       talloc_set_name_const(ctx, "auth_listener_pool");
 
        /*
         *      Now that we've sanity checked everything, receive the
@@ -1498,7 +1536,8 @@ static int auth_socket_recv(rad_listen_t *listener)
        packet = rad_recv(ctx, listener->fd, client->message_authenticator);
        if (!packet) {
                FR_STATS_INC(auth, total_malformed_requests);
-               DEBUG("%s", fr_strerror());
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
+               talloc_free(ctx);
                return 0;
        }
 
@@ -1528,7 +1567,7 @@ static int auth_socket_recv(rad_listen_t *listener)
 
        if (!request_receive(ctx, listener, packet, client, fun)) {
                FR_STATS_INC(auth, total_packets_dropped);
-               rad_free(&packet);
+               talloc_free(ctx);
                return 0;
        }
 
@@ -1557,6 +1596,7 @@ static int acct_socket_recv(rad_listen_t *listener)
        FR_STATS_INC(acct, total_requests);
 
        if (rcode < 20) {       /* RADIUS_HDR_LEN */
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                FR_STATS_INC(acct, total_malformed_requests);
                return 0;
        }
@@ -1604,6 +1644,7 @@ static int acct_socket_recv(rad_listen_t *listener)
                FR_STATS_INC(acct, total_packets_dropped);
                return 0;
        }
+       talloc_set_name_const(ctx, "acct_listener_pool");
 
        /*
         *      Now that we've sanity checked everything, receive the
@@ -1612,7 +1653,8 @@ static int acct_socket_recv(rad_listen_t *listener)
        packet = rad_recv(ctx, listener->fd, 0);
        if (!packet) {
                FR_STATS_INC(acct, total_malformed_requests);
-               ERROR("%s", fr_strerror());
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
+               talloc_free(ctx);
                return 0;
        }
 
@@ -1622,6 +1664,7 @@ static int acct_socket_recv(rad_listen_t *listener)
        if (!request_receive(ctx, listener, packet, client, fun)) {
                FR_STATS_INC(acct, total_packets_dropped);
                rad_free(&packet);
+               talloc_free(ctx);
                return 0;
        }
 
@@ -1640,13 +1683,13 @@ static int do_proxy(REQUEST *request)
                return 0;
        }
 
-       vp = pairfind(request->config_items, PW_HOME_SERVER_POOL, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_HOME_SERVER_POOL, 0, TAG_ANY);
 
        if (vp) {
                if (!home_pool_byname(vp->vp_strvalue, HOME_TYPE_COA)) {
                        REDEBUG2("Cannot proxy to unknown pool %s",
                                 vp->vp_strvalue);
-                       return 0;
+                       return -1;
                }
 
                return 1;
@@ -1655,8 +1698,8 @@ static int do_proxy(REQUEST *request)
        /*
         *      We have a destination IP address.  It will (later) proxied.
         */
-       vp = pairfind(request->config_items, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
-       if (!vp) vp = pairfind(request->config_items, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
+       if (!vp) vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY);
 
        if (!vp) return 0;
 
@@ -1670,6 +1713,7 @@ int rad_coa_recv(REQUEST *request)
 {
        int rcode = RLM_MODULE_OK;
        int ack, nak;
+       int proxy_status;
        VALUE_PAIR *vp;
 
        /*
@@ -1702,10 +1746,10 @@ int rad_coa_recv(REQUEST *request)
                 *      with Service-Type = Authorize-Only, it MUST
                 *      have a State attribute in it.
                 */
-               vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_SERVICE_TYPE, 0, TAG_ANY);
                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->vp_integer == PW_AUTHORIZE_ONLY)) {
+                               vp = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
                                if (!vp || (vp->vp_length == 0)) {
                                        REDEBUG("CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
                                        request->reply->code = PW_CODE_COA_NAK;
@@ -1738,24 +1782,35 @@ int rad_coa_recv(REQUEST *request)
                case RLM_MODULE_NOTFOUND:
                case RLM_MODULE_OK:
                case RLM_MODULE_UPDATED:
-                       if (do_proxy(request)) return RLM_MODULE_OK;
-                       request->reply->code = ack;
+                       proxy_status = do_proxy(request);
+                       if (proxy_status == 1) return RLM_MODULE_OK;
+
+                       if (proxy_status < 0) {
+                               request->reply->code = nak;
+                       } else {
+                               request->reply->code = ack;
+                       }
                        break;
                }
-       } else if (request->proxy_reply) {
+
+       }
+
+#ifdef WITH_PROXY
+       else if (request->proxy_reply) {
                /*
                 *      Start the reply code with the proxy reply
                 *      code.
                 */
                request->reply->code = request->proxy_reply->code;
        }
+#endif
 
        /*
         *      Copy State from the request to the reply.
         *      See RFC 5176 Section 3.3.
         */
-       vp = paircopy_by_num(request->reply, request->packet->vps, PW_STATE, 0, TAG_ANY);
-       if (vp) pairadd(&request->reply->vps, vp);
+       vp = fr_pair_list_copy_by_num(request->reply, request->packet->vps, PW_STATE, 0, TAG_ANY);
+       if (vp) fr_pair_add(&request->reply->vps, vp);
 
        /*
         *      We may want to over-ride the reply.
@@ -1824,7 +1879,7 @@ static int coa_socket_recv(rad_listen_t *listener)
        if (rcode < 0) return 0;
 
        if (rcode < 20) {       /* RADIUS_HDR_LEN */
-               FR_STATS_INC(coa, total_requests);
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                FR_STATS_INC(coa, total_malformed_requests);
                return 0;
        }
@@ -1865,6 +1920,7 @@ static int coa_socket_recv(rad_listen_t *listener)
                FR_STATS_INC(coa, total_packets_dropped);
                return 0;
        }
+       talloc_set_name_const(ctx, "coa_socket_recv_pool");
 
        /*
         *      Now that we've sanity checked everything, receive the
@@ -1873,13 +1929,15 @@ static int coa_socket_recv(rad_listen_t *listener)
        packet = rad_recv(ctx, listener->fd, client->message_authenticator);
        if (!packet) {
                FR_STATS_INC(coa, total_malformed_requests);
-               DEBUG("%s", fr_strerror());
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
+               talloc_free(ctx);
                return 0;
        }
 
        if (!request_receive(ctx, listener, packet, client, fun)) {
                FR_STATS_INC(coa, total_packets_dropped);
                rad_free(&packet);
+               talloc_free(ctx);
                return 0;
        }
 
@@ -1894,17 +1952,17 @@ static int coa_socket_recv(rad_listen_t *listener)
 static int proxy_socket_recv(rad_listen_t *listener)
 {
        RADIUS_PACKET   *packet;
+#ifdef WITH_TCP
+       listen_socket_t *sock;
+#endif
        char            buffer[128];
 
        packet = rad_recv(NULL, listener->fd, 0);
        if (!packet) {
-               ERROR("%s", fr_strerror());
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                return 0;
        }
 
-       /*
-        *      FIXME: Client MIB updates?
-        */
        switch (packet->code) {
        case PW_CODE_ACCESS_ACCEPT:
        case PW_CODE_ACCESS_CHALLENGE:
@@ -1933,11 +1991,22 @@ static int proxy_socket_recv(rad_listen_t *listener)
                       packet->code,
                       ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
                       packet->src_port, packet->id);
+#ifdef WITH_STATS
+               listener->stats.total_unknown_types++;
+#endif
                rad_free(&packet);
                return 0;
        }
 
+#ifdef WITH_TCP
+       sock = listener->data;
+       packet->proto = sock->proto;
+#endif
+
        if (!request_proxy_reply(packet)) {
+#ifdef WITH_STATS
+               listener->stats.total_packets_dropped++;
+#endif
                rad_free(&packet);
                return 0;
        }
@@ -2061,7 +2130,7 @@ static int client_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
                const char *identity = SSL_get_psk_identity(sock->ssn->ssl);
                if (identity) {
                        RDEBUG("Retrieved psk identity: %s", identity);
-                       pairmake_packet("TLS-PSK-Identity", identity, T_OP_SET);
+                       pair_make_request("TLS-PSK-Identity", identity, T_OP_SET);
                }
 #endif
        }
@@ -2266,6 +2335,17 @@ static int listen_bind(rad_listen_t *this)
                        break;
 #endif
 
+#ifdef WITH_DHCP
+               case RAD_LISTEN_DHCP:
+                       svp = getservbyname ("bootps", "udp");
+                       if (svp != NULL) {
+                               sock->my_port = ntohs(svp->s_port);
+                       } else {
+                               sock->my_port = 67;
+                       }
+                       break;
+#endif
+
                default:
                        WARN("Internal sanity check failed in binding to socket.  Ignoring problem");
                        return -1;
@@ -2601,10 +2681,23 @@ static int _listener_free(rad_listen_t *this)
                ) {
                listen_socket_t *sock = this->data;
 
-               rad_free(&sock->packet);
-
+               rad_assert(talloc_parent(sock) == this);
                rad_assert(sock->ev == NULL);
 
+               /*
+                *      Remove the child from the parent tree.
+                */
+               if (this->parent) {
+                       rbtree_deletebydata(this->parent->children, this);
+               }
+
+               /*
+                *      Delete / close all of the children, too!
+                */
+               if (this->children) {
+                       rbtree_walk(this->children, RBTREE_DELETE_ORDER, listener_unlink, this);
+               }
+
 #ifdef WITH_TLS
                /*
                 *      Note that we do NOT free this->tls, as the
@@ -2612,8 +2705,8 @@ static int _listener_free(rad_listen_t *this)
                 *      may be used by multiple listeners.
                 */
                if (this->tls) {
-                       TALLOC_FREE(sock->ssn);
-                       TALLOC_FREE(sock->request);
+                       rad_assert(!sock->ssn || (talloc_parent(sock->ssn) == sock));
+                       rad_assert(!sock->request || (talloc_parent(sock->request) == sock));
 #ifdef HAVE_PTHREAD_H
                        pthread_mutex_destroy(&(sock->mutex));
 #endif
@@ -2625,6 +2718,7 @@ static int _listener_free(rad_listen_t *this)
        return 0;
 }
 
+
 /*
  *     Allocate & initialize a new listener.
  */
@@ -2655,7 +2749,7 @@ 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_t *home, uint16_t src_port)
+rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t src_port)
 {
        time_t now;
        rad_listen_t *this;
@@ -2679,7 +2773,7 @@ rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port)
                return NULL;
        }
 
-       this = listen_alloc(main_config.config, RAD_LISTEN_PROXY);
+       this = listen_alloc(ctx, RAD_LISTEN_PROXY);
 
        sock = this->data;
        sock->other_ipaddr = home->ipaddr;
@@ -2695,10 +2789,6 @@ rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port)
         */
        this->print(this, buffer, sizeof(buffer));
 
-       if (debug_flag >= 2) {
-               DEBUG("Opening new proxy socket '%s'", buffer);
-       }
-
 #ifdef WITH_TCP
        sock->opened = sock->last_packet = now;
 
@@ -2712,15 +2802,15 @@ rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port)
                 *
                 *      http://www.developerweb.net/forum/showthread.php?p=13486
                 */
-               this->fd = fr_tcp_client_socket(&home->src_ipaddr,
-                                               &home->ipaddr, home->port);
+               this->fd = fr_socket_client_tcp(&home->src_ipaddr,
+                                               &home->ipaddr, home->port, false);
        } else
 #endif
                this->fd = fr_socket(&home->src_ipaddr, src_port);
 
        if (this->fd < 0) {
                this->print(this, buffer,sizeof(buffer));
-               ERROR("Failed opening proxy socket '%s' : %s",
+               ERROR("Failed opening new proxy socket '%s' : %s",
                      buffer, fr_strerror());
                home->last_failed_open = now;
                listen_free(&this);
@@ -2734,7 +2824,7 @@ rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port)
                DEBUG("Trying SSL to port %d\n", home->port);
                sock->ssn = tls_new_client_session(sock, home->tls, this->fd);
                if (!sock->ssn) {
-                       ERROR("Failed starting SSL to '%s'", buffer);
+                       ERROR("Failed starting SSL to new proxy socket '%s'", buffer);
                        home->last_failed_open = now;
                        listen_free(&this);
                        return NULL;
@@ -2769,6 +2859,12 @@ rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port)
                        listen_free(&this);
                        return NULL;
                }
+
+               this->print(this, buffer, sizeof(buffer));
+       }
+
+       if (rad_debug_lvl >= 3) {
+               DEBUG("Opened new proxy socket '%s'", buffer);
        }
 
        home->limit.num_connections++;
@@ -2981,14 +3077,7 @@ static void *recv_thread(void *arg)
  *     Generate a list of listeners.  Takes an input list of
  *     listeners, too, so we don't close sockets with waiting packets.
  */
-int listen_init(CONF_SECTION *config, rad_listen_t **head,
-#ifdef WITH_TLS
-               bool spawn_flag
-#else
-               UNUSED bool spawn_flag
-#endif
-               )
-
+int listen_init(CONF_SECTION *config, rad_listen_t **head, bool spawn_flag)
 {
        bool            override = false;
        CONF_SECTION    *cs = NULL;
@@ -2996,9 +3085,6 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
        rad_listen_t    *this;
        fr_ipaddr_t     server_ipaddr;
        uint16_t        auth_port = 0;
-#ifdef WITH_PROXY
-       bool            defined_proxy = false;
-#endif
 
        /*
         *      We shouldn't be called with a pre-existing list.
@@ -3053,7 +3139,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
                sock->my_ipaddr = server_ipaddr;
                sock->my_port = auth_port;
 
-               sock->clients = clients_parse_section(config, false);
+               sock->clients = client_list_parse_section(config, false);
                if (!sock->clients) {
                        cf_log_err_cs(config,
                                   "Failed to find any clients for this listen section");
@@ -3103,7 +3189,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
                sock->my_ipaddr = server_ipaddr;
                sock->my_port = auth_port + 1;
 
-               sock->clients = clients_parse_section(config, false);
+               sock->clients = client_list_parse_section(config, false);
                if (!sock->clients) {
                        cf_log_err_cs(config,
                                   "Failed to find any clients for this listen section");
@@ -3215,13 +3301,6 @@ add_sockets:
         *      add them to the event list.
         */
        for (this = *head; this != NULL; this = this->next) {
-#ifdef WITH_PROXY
-               if (this->type == RAD_LISTEN_PROXY) {
-                       defined_proxy = 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");
@@ -3271,58 +3350,11 @@ add_sockets:
                }
        }
 
-#ifdef WITH_TCP
-       if (!home_servers_udp) defined_proxy = true;
-#endif
-
-       /*
-        *      If we're proxying requests, open the proxy FD.
-        *      Otherwise, don't do anything.
-        */
-#ifdef WITH_PROXY
-       if ((main_config.proxy_requests == true) &&
-           !check_config &&
-           (*head != NULL) && !defined_proxy) {
-               uint16_t        port = 0;
-               home_server_t   home;
-
-               memset(&home, 0, sizeof(home));
-
-               /*
-                *      Open a default UDP port
-                */
-               home.proto = IPPROTO_UDP;
-               home.src_ipaddr = server_ipaddr;
-               port = 0;
-
-               /*
-                *      Address is still unspecified, use IPv4.
-                */
-               if (home.src_ipaddr.af == AF_UNSPEC) {
-                       home.src_ipaddr.af = AF_INET;
-                       /* everything else is already set to zero */
-               }
-
-               home.ipaddr.af = home.src_ipaddr.af;
-               /* everything else is already set to zero */
-
-               this = proxy_new_listener(&home, port);
-               if (!this) {
-                       listen_free(head);
-                       return -1;
-               }
-
-               radius_update_listener(this);
-       }
-#endif
-
        /*
         *      Haven't defined any sockets.  Die.
         */
        if (!*head) return -1;
 
-       xlat_register("listen", xlat_listen, NULL, NULL);
-
        return 0;
 }
 
index 0240451..3ead46e 100644 (file)
@@ -1,8 +1,4 @@
 /*
- * log.c       Logging module.
- *
- * 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
  *   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 2001,2006  The FreeRADIUS server project
- * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
- * Copyright 2000  Alan DeKok <aland@ox.org>
- * Copyright 2001  Chad Miller <cmiller@surfsouth.com>
  */
 
+/**
+ * $Id$
+ *
+ * @brief Logging functions used by the server core.
+ * @file main/log.c
+ *
+ * @copyright 2000,2006  The FreeRADIUS server project
+ * @copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
+ * @copyright 2000  Alan DeKok <aland@ox.org>
+ * @copyright 2001  Chad Miller <cmiller@surfsouth.com>
+ */
 RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
@@ -44,10 +46,10 @@ RCSID("$Id$")
 #include <pthread.h>
 #endif
 
-static bool rate_limit = true;
+log_lvl_t      rad_debug_lvl = 0;              //!< Global debugging level
+static bool    rate_limit = true;              //!< Whether repeated log entries should be rate limited
 
-/*
- * Logging facility names
+/** Maps log categories to message prefixes
  */
 static const FR_NAME_NUMBER levels[] = {
        { ": Debug: ",          L_DBG           },
@@ -64,11 +66,24 @@ static const FR_NAME_NUMBER levels[] = {
        { NULL, 0 }
 };
 
-#define VTC_RED                "\x1b[31m"
-#define VTC_YELLOW      "\x1b[33m"
-#define VTC_BOLD       "\x1b[1m"
-#define VTC_RESET      "\x1b[0m"
+/** @name VT100 escape sequences
+ *
+ * These sequences may be written to VT100 terminals to change the
+ * colour and style of the text.
+ *
+ @code{.c}
+   fprintf(stdout, VTC_RED "This text will be coloured red" VTC_RESET);
+ @endcode
+ * @{
+ */
+#define VTC_RED                "\x1b[31m"      //!< Colour following text red.
+#define VTC_YELLOW      "\x1b[33m"     //!< Colour following text yellow.
+#define VTC_BOLD       "\x1b[1m"       //!< Embolden following text.
+#define VTC_RESET      "\x1b[0m"       //!< Reset terminal text to default style/colour.
+/** @} */
 
+/** Maps log categories to VT100 style/colour escape sequences
+ */
 static const FR_NAME_NUMBER colours[] = {
        { "",                   L_DBG           },
        { VTC_BOLD,             L_AUTH          },
@@ -84,10 +99,15 @@ static const FR_NAME_NUMBER colours[] = {
        { NULL, 0 }
 };
 
-/*
- *     Syslog facility table.
+/** Syslog facility table
+ *
+ * Maps syslog facility keywords, to the syslog facility macros defined
+ * in the system's syslog.h.
+ *
+ * @note Not all facilities are supported by every operating system.
+ *       If a facility is unavailable it will not appear in the table.
  */
-const FR_NAME_NUMBER syslog_str2fac[] = {
+const FR_NAME_NUMBER syslog_facility_table[] = {
 #ifdef LOG_KERN
        { "kern",               LOG_KERN        },
 #endif
@@ -148,6 +168,40 @@ const FR_NAME_NUMBER syslog_str2fac[] = {
        { NULL,                 -1              }
 };
 
+/** Syslog severity table
+ *
+ * Maps syslog severity keywords, to the syslog severity macros defined
+ * in the system's syslog.h file.
+ *
+ */
+const FR_NAME_NUMBER syslog_severity_table[] = {
+#ifdef LOG_EMERG
+       { "emergency",          LOG_EMERG       },
+#endif
+#ifdef LOG_ALERT
+       { "alert",              LOG_ALERT       },
+#endif
+#ifdef LOG_CRIT
+       { "critical",           LOG_CRIT        },
+#endif
+#ifdef LOG_ERR
+       { "error",              LOG_ERR         },
+#endif
+#ifdef LOG_WARNING
+       { "warning",            LOG_WARNING     },
+#endif
+#ifdef LOG_NOTICE
+       { "notice",             LOG_NOTICE      },
+#endif
+#ifdef LOG_INFO
+       { "info",               LOG_INFO        },
+#endif
+#ifdef LOG_DEBUG
+       { "debug",              LOG_DEBUG       },
+#endif
+       { NULL,                 -1              }
+};
+
 const FR_NAME_NUMBER log_str2dst[] = {
        { "null",               L_DST_NULL      },
        { "files",              L_DST_FILES     },
@@ -172,7 +226,7 @@ static int stdout_fd = -1;  //!< The original unmolested stdout file descriptor
 
 static char const spaces[] = "                                                                                                                        ";
 
-/** On fault, reset STDOUT and STDERR to something useful.
+/** On fault, reset STDOUT and STDERR to something useful
  *
  * @return 0
  */
@@ -193,18 +247,6 @@ static int _restore_std(UNUSED int sig)
        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.
@@ -257,7 +299,7 @@ int radlog_init(fr_log_t *log, bool daemonize)
                 *      If we're debugging, allow STDERR to go to
                 *      STDOUT too, for executed programs,
                 */
-               if (debug_flag) {
+               if (rad_debug_lvl) {
                        dup2(STDOUT_FILENO, STDERR_FILENO);
                } else {
                        dup2(devnull, STDERR_FILENO);
@@ -271,7 +313,7 @@ int radlog_init(fr_log_t *log, bool daemonize)
                 *      If we're debugging, allow STDOUT to go to
                 *      STDERR too, for executed programs,
                 */
-               if (debug_flag) {
+               if (rad_debug_lvl) {
                        dup2(STDERR_FILENO, STDOUT_FILENO);
                } else {
                        dup2(devnull, STDOUT_FILENO);
@@ -286,7 +328,7 @@ int radlog_init(fr_log_t *log, bool daemonize)
                dup2(devnull, STDOUT_FILENO);
                dup2(devnull, STDERR_FILENO);
 
-       } else if (debug_flag) {
+       } else if (rad_debug_lvl) {
                /*
                 *      If we're debugging, allow STDOUT and STDERR to
                 *      go to the log file.
@@ -308,21 +350,18 @@ int radlog_init(fr_log_t *log, bool daemonize)
 
        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.
+/** Send a server log message to its destination
+ *
+ * @param type of log message.
+ * @param msg with printf style substitution tokens.
+ * @param ap Substitution arguments.
  */
-int vradlog(log_type_t type, char const *fmt, va_list ap)
+int vradlog(log_type_t type, char const *msg, va_list ap)
 {
        unsigned char *p;
        char buffer[10240];     /* The largest config item size, then extra for prefixes and suffixes */
@@ -362,7 +401,7 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
         *      of debugging.
         */
        if (default_log.dst != L_DST_SYSLOG) {
-               if ((debug_flag != 1) && (debug_flag != 2)) {
+               if ((rad_debug_lvl != 1) && (rad_debug_lvl != 2)) {
                        time_t timeval;
 
                        timeval = time(NULL);
@@ -388,7 +427,7 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
        }
 
        if (len < sizeof(buffer)) {
-               len += vsnprintf(buffer + len, sizeof(buffer) - len - 1, fmt, ap);
+               len += vsnprintf(buffer + len, sizeof(buffer) - len - 1, msg, ap);
        }
 
        /*
@@ -407,7 +446,7 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
                        continue;
 
                default:
-                       clen = fr_utf8_char(p);
+                       clen = fr_utf8_char(p, -1);
                        if (!clen) {
                                *p = '?';
                                continue;
@@ -435,21 +474,27 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
        case L_DST_SYSLOG:
                switch (type) {
                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_WARN:
+                       type = LOG_WARNING;
+                       break;
+
                case L_ERR:
                        type = LOG_ERR;
                        break;
@@ -471,6 +516,12 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
        return 0;
 }
 
+/** Send a server log message to its destination
+ *
+ * @param type of log message.
+ * @param msg with printf style substitution tokens.
+ * @param ... Substitution arguments.
+ */
 int radlog(log_type_t type, char const *msg, ...)
 {
        va_list ap;
@@ -481,7 +532,7 @@ int radlog(log_type_t type, char const *msg, ...)
        /*
         *      Non-debug message, or debugging is enabled.  Log it.
         */
-       if (((type & L_DBG) == 0) || (debug_flag > 0)) {
+       if (((type & L_DBG) == 0) || (rad_debug_lvl > 0)) {
                r = vradlog(type, msg, ap);
        }
        va_end(ap);
@@ -489,10 +540,14 @@ int radlog(log_type_t type, char const *msg, ...)
        return r;
 }
 
-/*
- *     Always log.
+/** Send a server log message to its destination without evaluating its debug level
+ *
+ * @param type of log message.
+ * @param msg with printf style substitution tokens.
+ * @param ... Substitution arguments.
  */
-static int CC_HINT(format (printf, 2, 3)) radlog_always(log_type_t type, char const *msg, ...)
+static int radlog_always(log_type_t type, char const *msg, ...) CC_HINT(format (printf, 2, 3));
+static int radlog_always(log_type_t type, char const *msg, ...)
 {
        va_list ap;
        int r;
@@ -504,20 +559,35 @@ static int CC_HINT(format (printf, 2, 3)) radlog_always(log_type_t type, char co
        return r;
 }
 
+/** Whether a server debug message should be logged
+ *
+ * @param type of message.
+ * @param lvl of debugging this message should be logged at.
+ * @return true if message should be logged, else false.
+ */
 inline bool debug_enabled(log_type_t type, log_lvl_t lvl)
 {
-       if ((type & L_DBG) && (lvl <= debug_flag)) return true;
+       if ((type & L_DBG) && (lvl <= rad_debug_lvl)) return true;
 
        return false;
 }
 
+/** Whether rate limiting is enabled
+ */
 bool rate_limit_enabled(void)
 {
-       if (rate_limit || (debug_flag < 1)) return true;
+       if (rate_limit || (rad_debug_lvl < 1)) return true;
 
        return false;
 }
 
+/** Whether a request specific debug message should be logged
+ *
+ * @param type of message.
+ * @param lvl of debugging this message should be logged at.
+ * @param request The current request.
+ * @return true if message should be logged, else false.
+ */
 inline bool radlog_debug_enabled(log_type_t type, log_lvl_t lvl, REQUEST *request)
 {
        /*
@@ -531,14 +601,22 @@ inline bool radlog_debug_enabled(log_type_t type, log_lvl_t lvl, REQUEST *reques
         *      then don't log the message.
         */
        if ((type & L_DBG) &&
-           ((request && request->log.func && (lvl <= request->log.lvl)) ||
-            ((debug_flag != 0) && (lvl <= debug_flag)))) {
+           ((request->log.func && (lvl <= request->log.lvl)) ||
+            ((rad_debug_lvl != 0) && (lvl <= rad_debug_lvl)))) {
                return true;
        }
 
        return false;
 }
 
+/** Send a log message to its destination, possibly including fields from the request
+ *
+ * @param type of log message, #L_ERR, #L_WARN, #L_INFO, #L_DBG.
+ * @param lvl Minimum required server or request level to output this message.
+ * @param request The current request.
+ * @param msg with printf style substitution tokens.
+ * @param ap Substitution arguments.
+ */
 void vradlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char const *msg, va_list ap)
 {
        size_t len = 0;
@@ -571,8 +649,9 @@ void vradlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char cons
                filename = default_log.debug_file;
                if (!filename)
 #endif
-
-               filename = default_log.file;
+               {
+                       filename = default_log.file;
+               }
        }
 
        if (filename) {
@@ -584,13 +663,13 @@ void vradlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char cons
                 *      This is SLOW!  Doing it for every log message
                 *      in every request is NOT recommended!
                 */
-
-                /* FIXME: escape chars! */
-               if (radius_xlat(buffer, sizeof(buffer), request, filename, NULL, NULL) < 0) {
-                       return;
-               }
+               if (radius_xlat(buffer, sizeof(buffer), request, filename, rad_filename_escape, NULL) < 0) return;
                request->log.func = rl;
 
+               /*
+                *      Ensure the directory structure exists, for
+                *      where we're going to write the log file.
+                */
                p = strrchr(buffer, FR_DIR_SEP);
                if (p) {
                        *p = '\0';
@@ -617,6 +696,9 @@ void vradlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char cons
        vsnprintf(buffer + len, sizeof(buffer) - len, msg, aq);
        va_end(aq);
 
+       /*
+        *      Make sure the indent isn't set to something crazy
+        */
        indent = request->log.indent > sizeof(spaces) ?
                 sizeof(spaces) :
                 request->log.indent;
@@ -691,9 +773,10 @@ void vradlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char cons
  * @see radlog_request_error for more details.
  *
  * @param type the log category.
- * @param lvl of debugging this message should be displayed at.
+ * @param lvl of debugging this message should be logged at.
  * @param request The current request.
- * @param msg format string.
+ * @param msg with printf style substitution tokens.
+ * @param ... Substitution arguments.
  */
 void radlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char const *msg, ...)
 {
@@ -720,9 +803,10 @@ void radlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char const
  * consistent behaviour.
  *
  * @param type the log category.
- * @param lvl of debugging this message should be displayed at.
+ * @param lvl of debugging this message should be logged at.
  * @param request The current request.
- * @param msg format string.
+ * @param msg with printf style substitution tokens.
+ * @param ... Substitution arguments.
  */
 void radlog_request_error(log_type_t type, log_lvl_t lvl, REQUEST *request, char const *msg, ...)
 {
@@ -737,17 +821,17 @@ void radlog_request_error(log_type_t type, log_lvl_t lvl, REQUEST *request, char
        va_end(ap);
 }
 
-/** Parse error, write out string we were parsing, and a message indicating the error
+/** Write the string being parsed, and a marker showing where the parse error occurred
  *
  * @param type the log category.
- * @param lvl of debugging this message should be displayed at.
+ * @param lvl of debugging this message should be logged at.
  * @param request The current request.
- * @param fmt string we were parsing.
+ * @param msg 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_lvl_t lvl, REQUEST *request,
-                          char const *fmt, size_t idx, char const *error)
+                          char const *msg, size_t idx, char const *error)
 {
        char const *prefix = "";
        uint8_t indent;
@@ -757,7 +841,7 @@ void radlog_request_marker(log_type_t type, log_lvl_t lvl, REQUEST *request,
        if (idx >= sizeof(spaces)) {
                size_t offset = (idx - (sizeof(spaces) - 1)) + (sizeof(spaces) * 0.75);
                idx -= offset;
-               fmt += offset;
+               msg += offset;
 
                prefix = "... ";
        }
@@ -768,7 +852,7 @@ void radlog_request_marker(log_type_t type, log_lvl_t lvl, REQUEST *request,
        indent = request->log.indent;
        request->log.indent = 0;
 
-       radlog_request(type, lvl, request, "%s%s", prefix, fmt);
+       radlog_request(type, lvl, request, "%s%s", prefix, msg);
        radlog_request(type, lvl, request, "%s%.*s^ %s", prefix, (int) idx, spaces, error);
 
        request->log.indent = indent;
@@ -780,16 +864,16 @@ void radlog_request_marker(log_type_t type, log_lvl_t lvl, REQUEST *request,
  * @note talloc_free must be called on the buffer returned in spaces and text
  *
  * Used to produce error messages such as this:
-@verbatim
-I'm a string with a parser # error
-                           ^ Unexpected character in string
-@endverbatim
+ @verbatim
+  I'm a string with a parser # error
+                             ^ Unexpected character in string
+ @endverbatim
  *
  * With code resembling this:
-@verbatim
-ERROR("%s", parsed_str);
-ERROR("%s^ %s", space, text);
-@endverbatim
+ @code{.c}
+   ERROR("%s", parsed_str);
+   ERROR("%s^ %s", space, text);
+ @endcode
  *
  * @todo merge with above function (radlog_request_marker)
  *
index 2025ed1..3046906 100644 (file)
@@ -25,6 +25,7 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
+#include <freeradius-devel/modpriv.h>
 #include <freeradius-devel/rad_assert.h>
 
 #include <sys/stat.h>
@@ -39,9 +40,9 @@ RCSID("$Id$")
 #  include <fcntl.h>
 #endif
 
-struct main_config_t   main_config;                            //!< Main server configuration.
+main_config_t          main_config;                            //!< Main server configuration.
 extern fr_cond_t       *debug_condition;
-fr_cond_t              *debug_condition;                       //!< Condition used to mark packets up for checking.
+fr_cond_t              *debug_condition = NULL;                        //!< Condition used to mark packets up for checking.
 bool                   event_loop_started = false;             //!< Whether the main event loop has been started yet.
 
 typedef struct cached_config_t {
@@ -82,44 +83,56 @@ static bool         do_colourise = false;
 
 static char const      *radius_dir = NULL;     //!< Path to raddb directory
 
+/**********************************************************************
+ *
+ *     We need to figure out where the logs go, before doing anything
+ *     else.  This is so that the log messages go to the correct
+ *     place.
+ *
+ *     BUT, we want the settings from the command line to over-ride
+ *     the ones in the configuration file.  So, these items are
+ *     parsed ONLY if there is no "-l foo" on the command line.
+ *
+ **********************************************************************/
 
 /*
- *  Security configuration for the server.
- */
-static const CONF_PARSER security_config[] = {
-       { "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
-       { "reject_delay",  FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) },
-       { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
-#ifdef ENABLE_OPENSSL_VERSION_CHECK
-       { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
-#endif
-       { NULL, -1, 0, NULL, NULL }
-};
-
-
-/*
- *     Logging configuration for the server.
+ *     Log destinations
  */
-static const CONF_PARSER logdest_config[] = {
+static const CONF_PARSER startup_log_config[] = {
        { "destination",  FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), "files" },
        { "syslog_facility",  FR_CONF_POINTER(PW_TYPE_STRING, &syslog_facility), STRINGIFY(0) },
 
+       { "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
        { "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 }
+       { "requests",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &default_log.file), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 
-static const CONF_PARSER serverdest_config[] = {
-       { "log",  FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) logdest_config },
+/*
+ *     Basic configuration for the server.
+ */
+static const CONF_PARSER startup_server_config[] = {
+       { "log",  FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) startup_log_config },
+
+       { "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
+       { "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
+
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 
-static const CONF_PARSER log_config_nodest[] = {
+/**********************************************************************
+ *
+ *     Now that we've parsed the log destination, AND the security
+ *     items, we can parse the rest of the configuration items.
+ *
+ **********************************************************************/
+static const CONF_PARSER log_config[] = {
        { "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" },
@@ -128,13 +141,24 @@ static const CONF_PARSER log_config_nodest[] = {
        { "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 }
+       { "msg_denied", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.denied_msg), "You are already logged in - access denied" },
+       CONF_PARSER_TERMINATOR
 };
 
 
+/*
+ *  Security configuration for the server.
+ */
+static const CONF_PARSER security_config[] = {
+       { "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
+       { "reject_delay",  FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) },
+       { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
+#ifdef ENABLE_OPENSSL_VERSION_CHECK
+       { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
+#endif
+       CONF_PARSER_TERMINATOR
+};
+
 static const CONF_PARSER resources[] = {
        /*
         *      Don't set a default here.  It's set in the code, below.  This means that
@@ -142,13 +166,9 @@ static const CONF_PARSER resources[] = {
         *      it exists.
         */
        { "talloc_pool_size", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.talloc_pool_size), NULL },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
-/*
- *  A mapping of configuration file names to internal variables
- */
 static const CONF_PARSER server_config[] = {
        /*
         *      FIXME: 'prefix' is the ONLY one which should be
@@ -178,7 +198,7 @@ static const CONF_PARSER server_config[] = {
 #ifdef WITH_PROXY
        { "proxy_requests", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.proxy_requests), "yes" },
 #endif
-       { "log", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) log_config_nodest },
+       { "log", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) log_config },
 
        { "resources", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) resources },
 
@@ -196,10 +216,23 @@ static const CONF_PARSER server_config[] = {
        { "log_stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &log_stripped_names), NULL },
 
        {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) security_config },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
+
+/**********************************************************************
+ *
+ *     The next few items are here as a "bootstrap" for security.
+ *     They allow the server to switch users, chroot, while still
+ *     opening the various output files with the correct permission.
+ *
+ *     It's rare (or impossible) to have parse errors here, so we
+ *     don't worry too much about that.  In contrast, when we parse
+ *     the rest of the configuration, we CAN get parse errors.  We
+ *     want THOSE parse errors to go to the log file, and we want the
+ *     log file to have the correct permissions.
+ *
+ **********************************************************************/
 static const CONF_PARSER bootstrap_security_config[] = {
 #ifdef HAVE_SETUID
        { "user",  FR_CONF_POINTER(PW_TYPE_STRING, &uid_name), NULL },
@@ -207,13 +240,19 @@ static const CONF_PARSER bootstrap_security_config[] = {
 #endif
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER bootstrap_config[] = {
        {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) bootstrap_security_config },
 
+       { "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"},
+
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
+       { "run_dir", FR_CONF_POINTER(PW_TYPE_STRING, &run_dir), "${localstatedir}/run/${name}"},
+
        /*
         *      For backwards compatibility.
         */
@@ -223,10 +262,10 @@ static const CONF_PARSER bootstrap_config[] = {
 #endif
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
+
 static size_t config_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
 {
        size_t len = 0;
@@ -379,7 +418,7 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        }
 
        strlcpy(buffer, p, (q + 1) - p);
-       if (fr_pton(&ip, buffer, -1, false) <= 0) {
+       if (fr_pton(&ip, buffer, -1, AF_UNSPEC, false) < 0) {
                REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", buffer);
                goto error;
        }
@@ -411,6 +450,35 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        return -1;
 }
 
+/*
+ *     Xlat for %{listen:foo}
+ */
+static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
+                          char const *fmt, char *out, size_t outlen)
+{
+       char const *value = NULL;
+       CONF_PAIR *cp;
+
+       if (!fmt || !out || (outlen < 1)) return 0;
+
+       if (!request->listener) {
+               RWDEBUG("No listener associated with this request");
+               *out = '\0';
+               return 0;
+       }
+
+       cp = cf_pair_find(request->listener->cs, fmt);
+       if (!cp || !(value = cf_pair_value(cp))) {
+               RDEBUG("Listener does not contain config item \"%s\"", fmt);
+               *out = '\0';
+               return 0;
+       }
+
+       strlcpy(out, value, outlen);
+
+       return strlen(out);
+}
+
 #ifdef HAVE_SETUID
 /*
  *  Do chroot, if requested.
@@ -419,6 +487,9 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
  */
 static int switch_users(CONF_SECTION *cs)
 {
+       bool do_suid = false;
+       bool do_sgid = false;
+
        /*
         *      Get the current maximum for core files.  Do this
         *      before anything else so as to ensure it's properly
@@ -433,32 +504,41 @@ static int switch_users(CONF_SECTION *cs)
         *      Don't do chroot/setuid/setgid if we're in debugging
         *      as non-root.
         */
-       if (debug_flag && (getuid() != 0)) return 1;
+       if (rad_debug_lvl && (getuid() != 0)) return 1;
 
        if (cf_section_parse(cs, NULL, bootstrap_config) < 0) {
                fprintf(stderr, "radiusd: Error: Failed to parse user/group information.\n");
                return 0;
        }
 
-
 #ifdef HAVE_GRP_H
-       /*  Set GID.  */
+       /*
+        *      Get the correct GID for the server.
+        */
+       server_gid = getgid();
+
        if (gid_name) {
                struct group *gr;
 
                gr = getgrnam(gid_name);
-               if (gr == NULL) {
+               if (!gr) {
                        fprintf(stderr, "%s: Cannot get ID for group %s: %s\n",
                                progname, gid_name, fr_syserror(errno));
                        return 0;
                }
-               server_gid = gr->gr_gid;
-       } else {
-               server_gid = getgid();
+
+               if (server_gid != gr->gr_gid) {
+                       server_gid = gr->gr_gid;
+                       do_sgid = true;
+               }
        }
 #endif
 
-       /*  Set UID.  */
+       /*
+        *      Get the correct UID for the server.
+        */
+       server_uid = getuid();
+
        if (uid_name) {
                struct passwd *user;
 
@@ -468,11 +548,12 @@ static int switch_users(CONF_SECTION *cs)
                        return 0;
                }
 
-               if (getuid() == user->pw_uid) {
-                       uid_name = NULL;
-               } else {
-
+               /*
+                *      We're not the correct user.  Go set that.
+                */
+               if (server_uid != user->pw_uid) {
                        server_uid = user->pw_uid;
+                       do_suid = true;
 #ifdef HAVE_INITGROUPS
                        if (initgroups(uid_name, server_gid) < 0) {
                                fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n",
@@ -482,11 +563,13 @@ static int switch_users(CONF_SECTION *cs)
                        }
 #endif
                }
+
                talloc_free(user);
-       } else {
-               server_uid = getuid();
        }
 
+       /*
+        *      Do chroot BEFORE changing UIDs.
+        */
        if (chroot_dir) {
                if (chroot(chroot_dir) < 0) {
                        fprintf(stderr, "%s: Failed to perform chroot %s: %s",
@@ -512,45 +595,91 @@ static int switch_users(CONF_SECTION *cs)
        }
 
 #ifdef HAVE_GRP_H
-       /*  Set GID.  */
-       if (gid_name && (setgid(server_gid) < 0)) {
-               fprintf(stderr, "%s: Failed setting group to %s: %s",
-                       progname, gid_name, fr_syserror(errno));
-               return 0;
+       /*
+        *      Set the GID.  Don't bother checking it.
+        */
+       if (do_sgid) {
+               if (setgid(server_gid) < 0){
+                       fprintf(stderr, "%s: Failed setting group to %s: %s",
+                               progname, gid_name, fr_syserror(errno));
+                       return 0;
+               }
        }
 #endif
 
        /*
-        *      Just before losing root permissions, ensure that the
-        *      log files have the correct owner && group.
+        *      The directories for PID files and logs must exist.  We
+        *      need to create them if we're told to write files to
+        *      those directories.
+        *
+        *      Because this creation is new in 3.0.9, it's a soft
+        *      fail.
         *
-        *      We have to do this because the log file MAY have been
-        *      specified on the command-line.
         */
-       if (uid_name || gid_name) {
-               if ((default_log.dst == L_DST_FILES) &&
-                   (default_log.fd < 0)) {
-                       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", main_config.log_file, fr_syserror(errno));
-                               return 0;
-                       }
+       if (main_config.write_pid) {
+               char *my_dir;
 
-                       if (chown(main_config.log_file, server_uid, server_gid) < 0) {
-                               fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
-                                       progname, main_config.log_file, fr_syserror(errno));
-                               return 0;
-                       }
+               my_dir = talloc_strdup(NULL, run_dir);
+               if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
+                       DEBUG("Failed to create run_dir %s: %s",
+                             my_dir, strerror(errno));
                }
+               talloc_free(my_dir);
        }
 
-       if (uid_name) {
+       if (default_log.dst == L_DST_FILES) {
+               char *my_dir;
+
+               my_dir = talloc_strdup(NULL, radlog_dir);
+               if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
+                       DEBUG("Failed to create logdir %s: %s",
+                             my_dir, strerror(errno));
+               }
+               talloc_free(my_dir);
+       }
+
+       /*
+        *      Once we're done with all of the privileged work,
+        *      permanently change the UID.
+        */
+       if (do_suid) {
                rad_suid_set_down_uid(server_uid);
                rad_suid_down();
        }
 
        /*
+        *      If we don't already have a log file open, open one
+        *      now.  We may not have been logging anything yet.  The
+        *      server normally starts up fairly quietly.
+        */
+       if ((default_log.dst == L_DST_FILES) &&
+           (default_log.fd < 0)) {
+               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", main_config.log_file, fr_syserror(errno));
+                       return 0;
+               }
+       }
+
+       /*
+        *      If we need to change UID, ensure that the log files
+        *      have the correct owner && group.
+        *
+        *      We have to do this because some log files MAY already
+        *      have been written as root.  We need to change them to
+        *      have the correct ownership before proceeding.
+        */
+       if ((do_suid || do_sgid) &&
+           (default_log.dst == L_DST_FILES)) {
+               if (fchown(default_log.fd, server_uid, server_gid) < 0) {
+                       fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
+                               progname, main_config.log_file, fr_syserror(errno));
+                       return 0;
+               }
+       }
+
+       /*
         *      This also clears the dumpable flag if core dumps
         *      aren't allowed.
         */
@@ -643,7 +772,7 @@ int main_config_init(void)
         *
         *      Which should be enough for many configurations.
         */
-       main_config.talloc_pool_size = 32 * 1024; /* default */
+       main_config.talloc_pool_size = 8 * 1024; /* default */
 
        /*
         *      Read the distribution dictionaries first, then
@@ -720,12 +849,10 @@ do {\
        version_init_numbers(subcs);
 
        /* Read the configuration file */
-       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, main_config.name);
+       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
        if (cf_file_read(cs, buffer) < 0) {
                ERROR("Errors reading or parsing %s", buffer);
                talloc_free(cs);
-
                return -1;
        }
 
@@ -734,7 +861,7 @@ do {\
         *      set it now.
         */
        if (default_log.dst == L_DST_NULL) {
-               if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
+               if (cf_section_parse(cs, NULL, startup_server_config) < 0) {
                        fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
                        cf_file_free(cs);
                        return -1;
@@ -765,7 +892,7 @@ do {\
                                cf_file_free(cs);
                                return -1;
                        }
-                       main_config.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
+                       main_config.syslog_facility = fr_str2int(syslog_facility_table, syslog_facility, -1);
                        if (main_config.syslog_facility < 0) {
                                fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
                                        syslog_facility);
@@ -798,22 +925,6 @@ do {\
 #endif
 
        /*
-        *      Open the log file AFTER switching uid / gid.  If we
-        *      did switch uid/gid, then the code in switch_users()
-        *      took care of setting the file permissions correctly.
-        */
-       if ((default_log.dst == L_DST_FILES) &&
-           (default_log.fd < 0)) {
-               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", main_config.log_file, fr_syserror(errno));
-                       cf_file_free(cs);
-                       return -1;
-               }
-       }
-
-       /*
         *      This allows us to figure out where, relative to
         *      radiusd.conf, the other configuration files exist.
         */
@@ -835,12 +946,13 @@ do {\
         *      command-line: use whatever is in the config
         *      file.
         */
-       if (debug_flag == 0) {
-               debug_flag = main_config.debug_level;
+       if (rad_debug_lvl == 0) {
+               rad_debug_lvl = main_config.debug_level;
        }
-       fr_debug_flag = debug_flag;
+       fr_debug_lvl = rad_debug_lvl;
 
-       FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time, (main_config.max_request_time != 0), 100);
+       FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time,
+                             (main_config.max_request_time != 0), 100);
 
        /*
         *      reject_delay can be zero.  OR 1 though 10.
@@ -852,8 +964,8 @@ do {\
 
        FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 10);
 
-       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, >=, 16*1024);
-       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, <=, 1024*1024);
+       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, >=, 2 * 1024);
+       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, <=, 1024 * 1024);
 
        /*
         * Set default initial request processing delay to 1/3 of a second.
@@ -879,16 +991,17 @@ do {\
        }
 
        DEBUG2("%s: #### Loading Clients ####", main_config.name);
-       if (!clients_parse_section(cs, false)) {
+       if (!client_list_parse_section(cs, false)) {
                return -1;
        }
 
        /*
-        *  Register the %{config:section.subsection} xlat function.
+        *      Register the %{config:section.subsection} xlat function.
         */
        xlat_register("config", xlat_config, NULL, NULL);
        xlat_register("client", xlat_client, NULL, NULL);
        xlat_register("getclient", xlat_getclient, NULL, NULL);
+       xlat_register("listen", xlat_listen, NULL, NULL);
 
        /*
         *  Go update our behaviour, based on the configuration
@@ -933,7 +1046,7 @@ int main_config_free(void)
         *      Clean up the configuration data
         *      structures.
         */
-       clients_free(NULL);
+       client_list_free(NULL);
        realms_free();
        listen_free(&main_config.listen);
 
@@ -969,20 +1082,105 @@ void hup_logfile(void)
        }
 }
 
+static int hup_callback(void *ctx, void *data)
+{
+       CONF_SECTION *modules = ctx;
+       CONF_SECTION *cs = data;
+       CONF_SECTION *parent;
+       char const *name;
+       module_instance_t *mi;
+
+       /*
+        *      Files may be defined in sub-sections of a module
+        *      config.  Walk up the tree until we find the module
+        *      definition.
+        */
+       parent = cf_item_parent(cf_section_to_item(cs));
+       while (parent != modules) {
+               cs = parent;
+               parent = cf_item_parent(cf_section_to_item(cs));
+
+               /*
+                *      Something went wrong.  Oh well...
+                */
+               if (!parent) return 0;
+       }
+
+       name = cf_section_name2(cs);
+       if (!name) name = cf_section_name1(cs);
+
+       mi = module_find(modules, name);
+       if (!mi) return 0;
+
+       if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) return 0;
+
+       if (!module_hup_module(mi->cs, mi, time(NULL))) return 0;
+
+       return 0;
+}
+
 void main_config_hup(void)
 {
+       int rcode;
        cached_config_t *cc;
        CONF_SECTION *cs;
+       time_t when;
        char buffer[1024];
 
-       INFO("HUP - Re-reading configuration files");
+       static time_t last_hup = 0;
+
+       /*
+        *      Re-open the log file.  If we can't, then keep logging
+        *      to the old log file.
+        *
+        *      The "open log file" code is here rather than in log.c,
+        *      because it makes that function MUCH simpler.
+        */
+       hup_logfile();
+
+       /*
+        *      Only check the config files every few seconds.
+        */
+       when = time(NULL);
+       if ((last_hup + 2) >= when) {
+               INFO("HUP - Last HUP was too recent.  Ignoring");
+               return;
+       }
+       last_hup = when;
+
+       rcode = cf_file_changed(cs_cache->cs, hup_callback);
+       if (rcode == CF_FILE_NONE) {
+               INFO("HUP - No files changed.  Ignoring");
+               return;
+       }
+
+       if (rcode == CF_FILE_ERROR) {
+               INFO("HUP - Cannot read configuration files.  Ignoring");
+               return;
+       }
+
+       /*
+        *      No config files have changed.
+        */
+       if ((rcode & CF_FILE_CONFIG) == 0) {
+               if ((rcode & CF_FILE_MODULE) != 0) {
+                       INFO("HUP - Files loaded by a module have changed.");
+
+                       /*
+                        *      FIXME: reload the module.
+                        */
+
+               }
+               return;
+       }
 
        cs = cf_section_alloc(NULL, "main", NULL);
        if (!cs) return;
 
        /* Read the configuration file */
-       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, main_config.name);
+       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
+
+       INFO("HUP - Re-reading configuration files");
        if (cf_file_read(cs, buffer) < 0) {
                ERROR("Failed to re-read or parse %s", buffer);
                talloc_free(cs);
@@ -1009,15 +1207,6 @@ void main_config_hup(void)
        cc->next = cs_cache;
        cs_cache = cc;
 
-       /*
-        *      Re-open the log file.  If we can't, then keep logging
-        *      to the old log file.
-        *
-        *      The "open log file" code is here rather than in log.c,
-        *      because it makes that function MUCH simpler.
-        */
-       hup_logfile();
-
        INFO("HUP - loading modules");
 
        /*
index b165826..bbe3a63 100644 (file)
@@ -33,6 +33,20 @@ RCSID("$Id$")
 
 #include <ctype.h>
 
+#if 0
+static void map_dump(REQUEST *request, vp_map_t const *map)
+{
+       RDEBUG(">>> MAP TYPES LHS: %s, RHS: %s",
+              fr_int2str(tmpl_names, map->lhs->type, "???"),
+              fr_int2str(tmpl_names, map->rhs->type, "???"));
+
+       if (map->rhs) {
+               RDEBUG(">>> MAP NAMES %s %s", map->lhs->name, map->rhs->name);
+       }
+}
+#endif
+
+
 /** re-parse a map where the lhs is an unknown attribute.
  *
  *
@@ -40,15 +54,17 @@ RCSID("$Id$")
  * @param rhs_type quotation type around rhs.
  * @param rhs string to re-parse.
  */
-bool map_cast_from_hex(value_pair_map_t *map, FR_TOKEN rhs_type, char const *rhs)
+bool map_cast_from_hex(vp_map_t *map, FR_TOKEN rhs_type, char const *rhs)
 {
        size_t len;
        ssize_t rlen;
        uint8_t *ptr;
+       char const *p;
+       pair_lists_t list;
 
        DICT_ATTR const *da;
        VALUE_PAIR *vp;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        rad_assert(map != NULL);
 
@@ -92,7 +108,7 @@ bool map_cast_from_hex(value_pair_map_t *map, FR_TOKEN rhs_type, char const *rhs
 
        if ((size_t) rlen < len) {
        free_vp:
-               pairfree(&vp);
+               fr_pair_list_free(&vp);
                return false;
        }
 
@@ -111,11 +127,16 @@ bool map_cast_from_hex(value_pair_map_t *map, FR_TOKEN rhs_type, char const *rhs
        map->rhs->tmpl_data_type = da->type;
        map->rhs->tmpl_data_length = vp->vp_length;
        if (vp->da->flags.is_pointer) {
-               map->rhs->tmpl_data_value.ptr = talloc_memdup(map->rhs, vp->data.ptr, vp->vp_length);
+               if (vp->da->type == PW_TYPE_STRING) {
+                       map->rhs->tmpl_data_value.ptr = talloc_bstrndup(map->rhs, vp->data.ptr, vp->vp_length);
+               } else {
+                       map->rhs->tmpl_data_value.ptr = talloc_memdup(map->rhs, vp->data.ptr, vp->vp_length);
+               }
        } else {
                memcpy(&map->rhs->tmpl_data_value, &vp->data, sizeof(map->rhs->tmpl_data_value));
        }
        map->rhs->name = vp_aprints_value(map->rhs, vp, '"');
+       map->rhs->len = talloc_array_length(map->rhs->name) - 1;
 
        /*
         *      Set the LHS to the REAL attribute name.
@@ -124,17 +145,35 @@ bool map_cast_from_hex(value_pair_map_t *map, FR_TOKEN rhs_type, char const *rhs
        memcpy(&vpt->data.attribute, &map->lhs->data.attribute, sizeof(vpt->data.attribute));
        vpt->tmpl_da = da;
 
+       /*
+        *      Be sure to keep the "&control:" or "control:" prefix.
+        *      If it's there, we re-generate it from whatever was in
+        *      the original name, including the '&'.
+        */
+       p = map->lhs->name;
+       if (*p == '&') p++;
+       len = radius_list_name(&list, p, PAIR_LIST_UNKNOWN);
+
+       if (list != PAIR_LIST_UNKNOWN) {
+               rad_const_free(vpt->name);
+
+               vpt->name = talloc_asprintf(vpt, "%.*s:%s",
+                                           (int) len, map->lhs->name,
+                                           map->lhs->tmpl_da->name);
+               vpt->len = strlen(vpt->name);
+       }
+
        talloc_free(map->lhs);
        map->lhs = vpt;
 
-       pairfree(&vp);
+       fr_pair_list_free(&vp);
 
        VERIFY_MAP(map);
 
        return true;
 }
 
-/** Convert CONFIG_PAIR (which may contain refs) to value_pair_map_t.
+/** Convert CONFIG_PAIR (which may contain refs) to vp_map_t.
  *
  * Treats the left operand as an attribute reference
  * @verbatim<request>.<list>.<attribute>@endverbatim
@@ -156,13 +195,13 @@ bool map_cast_from_hex(value_pair_map_t *map, FR_TOKEN rhs_type, char const *rhs
  *     references in.
  * @param[in] src_list_def The default list to resolve unqualified attributes
  *     in.
- * @return value_pair_map_t if successful or NULL on error.
+ * @return vp_map_t if successful or NULL on error.
  */
-int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
+int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
                 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_map_t *map;
+       vp_map_t *map;
        char const *attr, *value;
        ssize_t slen;
        FR_TOKEN type;
@@ -171,7 +210,7 @@ int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
 
        if (!cp) return -1;
 
-       map = talloc_zero(ctx, value_pair_map_t);
+       map = talloc_zero(ctx, vp_map_t);
        map->op = cf_pair_operator(cp);
        map->ci = cf_pair_to_item(cp);
 
@@ -191,7 +230,7 @@ int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
        case T_DOUBLE_QUOTED_STRING:
        case T_BACK_QUOTED_STRING:
                slen = tmpl_afrom_str(ctx, &map->lhs, attr, talloc_array_length(attr) - 1,
-                                     type, dst_request_def, dst_list_def);
+                                     type, dst_request_def, dst_list_def, true);
                if (slen <= 0) {
                        char *spaces, *text;
 
@@ -214,11 +253,12 @@ int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
                        goto marker;
                }
 
-               if (!tmpl_define_unknown_attr(map->lhs)) {
+               if (tmpl_define_unknown_attr(map->lhs) < 0) {
                        cf_log_err_cp(cp, "Failed creating attribute %s: %s",
                                      map->lhs->name, fr_strerror());
                        goto error;
                }
+
                break;
        }
 
@@ -227,13 +267,15 @@ int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
         */
        type = cf_pair_value_type(cp);
 
-       if ((type == T_BARE_WORD) && (value[0] == '0') && (tolower((int)value[1] == 'x')) &&
-               map_cast_from_hex(map, type, value)) {
-               /* do nothing */
+       if ((map->lhs->type == TMPL_TYPE_ATTR) &&
+           map->lhs->tmpl_da->flags.is_unknown &&
+           !map_cast_from_hex(map, type, value)) {
+               goto error;
+
        } else {
-               slen = tmpl_afrom_str(map, &map->rhs, value, strlen(value), type, src_request_def, src_list_def);
+               slen = tmpl_afrom_str(map, &map->rhs, value, strlen(value), type, src_request_def, src_list_def, true);
                if (slen < 0) goto marker;
-               if (!tmpl_define_unknown_attr(map->rhs)) {
+               if (tmpl_define_unknown_attr(map->rhs) < 0) {
                        cf_log_err_cp(cp, "Failed creating attribute %s: %s", map->rhs->name, fr_strerror());
                        goto error;
                }
@@ -243,6 +285,16 @@ int map_afrom_cp(TALLOC_CTX *ctx, value_pair_map_t **out, CONF_PAIR *cp,
                goto error;
        }
 
+       /*
+        *      We cannot assign a count to an attribute.  That must
+        *      be done in an xlat.
+        */
+       if ((map->rhs->type == TMPL_TYPE_ATTR) &&
+           (map->rhs->tmpl_num == NUM_COUNT)) {
+               cf_log_err_cp(cp, "Cannot assign from a count");
+               goto error;
+       }
+
        VERIFY_MAP(map);
 
        *out = map;
@@ -269,7 +321,7 @@ error:
  * @param[in] max number of mappings to process.
  * @return -1 on error, else 0.
  */
-int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs,
+int map_afrom_cs(vp_map_t **out, CONF_SECTION *cs,
                 pair_lists_t dst_list_def, pair_lists_t src_list_def,
                 map_validate_t validate, void *ctx,
                 unsigned int max)
@@ -282,14 +334,12 @@ int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs,
        CONF_PAIR *cp;
 
        unsigned int total = 0;
-       value_pair_map_t **tail, *map;
+       vp_map_t **tail, *map;
        TALLOC_CTX *parent;
 
        *out = NULL;
        tail = out;
 
-       if (!cs) return 0;
-
        /*
         *      The first map has cs as the parent.
         *      The rest have the previous map as the parent.
@@ -319,7 +369,9 @@ int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs,
             ci = cf_item_find_next(cs, ci)) {
                if (total++ == max) {
                        cf_log_err(ci, "Map size exceeded");
-                       goto error;
+               error:
+                       TALLOC_FREE(*out);
+                       return -1;
                }
 
                if (!cf_item_is_pair(ci)) {
@@ -344,13 +396,11 @@ int map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs,
        }
 
        return 0;
-error:
-       TALLOC_FREE(*out);
-       return -1;
+
 }
 
 
-/** Convert strings to value_pair_map_t
+/** Convert strings to vp_map_t
  *
  * Treatment of operands depends on quotation, barewords are treated
  * as attribute references, double quoted values are treated as
@@ -374,9 +424,9 @@ error:
  *     references in.
  * @param[in] src_list_def The default list to resolve unqualified attributes
  *     in.
- * @return value_pair_map_t if successful or NULL on error.
+ * @return vp_map_t if successful or NULL on error.
  */
-int map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, FR_TOKEN lhs_type,
+int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out, char const *lhs, FR_TOKEN lhs_type,
                     FR_TOKEN op, char const *rhs, FR_TOKEN rhs_type,
                     request_refs_t dst_request_def,
                     pair_lists_t dst_list_def,
@@ -384,11 +434,11 @@ int map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, F
                     pair_lists_t src_list_def)
 {
        ssize_t slen;
-       value_pair_map_t *map;
+       vp_map_t *map;
 
-       map = talloc_zero(ctx, value_pair_map_t);
+       map = talloc_zero(ctx, vp_map_t);
 
-       slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, dst_request_def, dst_list_def);
+       slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, dst_request_def, dst_list_def, true);
        if (slen < 0) {
        error:
                talloc_free(map);
@@ -403,7 +453,7 @@ int map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, F
                return 0;
        }
 
-       slen = tmpl_afrom_str(map, &map->rhs, rhs, strlen(rhs), rhs_type, src_request_def, src_list_def);
+       slen = tmpl_afrom_str(map, &map->rhs, rhs, strlen(rhs), rhs_type, src_request_def, src_list_def, true);
        if (slen < 0) goto error;
 
        VERIFY_MAP(map);
@@ -416,7 +466,7 @@ int map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, F
 /** Convert a value pair string to valuepair map
  *
  * Takes a valuepair string with list and request qualifiers and converts it into a
- * value_pair_map_t.
+ * vp_map_t.
  *
  * @param ctx where to allocate the map.
  * @param out Where to write the new map (must be freed with talloc_free()).
@@ -427,7 +477,7 @@ int map_afrom_fields(TALLOC_CTX *ctx, value_pair_map_t **out, char const *lhs, F
  * @param src_list_def to use if attribute isn't qualified.
  * @return 0 on success, < 0 on error.
  */
-int map_afrom_attr_str(TALLOC_CTX *ctx, value_pair_map_t **out, char const *vp_str,
+int map_afrom_attr_str(TALLOC_CTX *ctx, vp_map_t **out, char const *vp_str,
                       request_refs_t dst_request_def, pair_lists_t dst_list_def,
                       request_refs_t src_request_def, pair_lists_t src_list_def)
 {
@@ -435,7 +485,7 @@ int map_afrom_attr_str(TALLOC_CTX *ctx, value_pair_map_t **out, char const *vp_s
        FR_TOKEN quote;
 
        VALUE_PAIR_RAW raw;
-       value_pair_map_t *map = NULL;
+       vp_map_t *map = NULL;
 
        quote = gettoken(&p, raw.l_opand, sizeof(raw.l_opand), false);
        switch (quote) {
@@ -474,17 +524,139 @@ int map_afrom_attr_str(TALLOC_CTX *ctx, value_pair_map_t **out, char const *vp_s
        return 0;
 }
 
+/** Compare map where LHS is #TMPL_TYPE_ATTR
+ *
+ * Compares maps by lhs->tmpl_da, lhs->tmpl_tag, lhs->tmpl_num
+ *
+ * @note both map->lhs must be #TMPL_TYPE_ATTR.
+ *
+ * @param a first map.
+ * @param b second map.
+ */
+int8_t map_cmp_by_lhs_attr(void const *a, void const *b)
+{
+       vp_tmpl_t const *my_a = ((vp_map_t const *)a)->lhs;
+       vp_tmpl_t const *my_b = ((vp_map_t const *)b)->lhs;
+
+       VERIFY_TMPL(my_a);
+       VERIFY_TMPL(my_b);
+
+       uint8_t cmp;
+
+       rad_assert(my_a->type == TMPL_TYPE_ATTR);
+       rad_assert(my_b->type == TMPL_TYPE_ATTR);
+
+       cmp = fr_pointer_cmp(my_a->tmpl_da, my_b->tmpl_da);
+       if (cmp != 0) return cmp;
+
+       if (my_a->tmpl_tag < my_b->tmpl_tag) return -1;
+
+       if (my_a->tmpl_tag > my_b->tmpl_tag) return 1;
+
+       if (my_a->tmpl_num < my_b->tmpl_num) return -1;
+
+       if (my_a->tmpl_num > my_b->tmpl_num) return 1;
+
+       return 0;
+}
+
+static void map_sort_split(vp_map_t *source, vp_map_t **front, vp_map_t **back)
+{
+       vp_map_t *fast;
+       vp_map_t *slow;
+
+       /*
+        *      Stopping condition - no more elements left to split
+        */
+       if (!source || !source->next) {
+               *front = source;
+               *back = NULL;
+
+               return;
+       }
+
+       /*
+        *      Fast advances twice as fast as slow, so when it gets to the end,
+        *      slow will point to the middle of the linked list.
+        */
+       slow = source;
+       fast = source->next;
+
+       while (fast) {
+               fast = fast->next;
+               if (fast) {
+                       slow = slow->next;
+                       fast = fast->next;
+               }
+       }
+
+       *front = source;
+       *back = slow->next;
+       slow->next = NULL;
+}
+
+static vp_map_t *map_sort_merge(vp_map_t *a, vp_map_t *b, fr_cmp_t cmp)
+{
+       vp_map_t *result = NULL;
+
+       if (!a) return b;
+       if (!b) return a;
+
+       /*
+        *      Compare things in the maps
+        */
+       if (cmp(a, b) <= 0) {
+               result = a;
+               result->next = map_sort_merge(a->next, b, cmp);
+       } else {
+               result = b;
+               result->next = map_sort_merge(a, b->next, cmp);
+       }
+
+       return result;
+}
+
+/** Sort a linked list of #vp_map_t using merge sort
+ *
+ * @param[in,out] maps List of #vp_map_t to sort.
+ * @param[in] cmp to sort with
+ */
+void map_sort(vp_map_t **maps, fr_cmp_t cmp)
+{
+       vp_map_t *head = *maps;
+       vp_map_t *a;
+       vp_map_t *b;
+
+       /*
+        *      If there's 0-1 elements it must already be sorted.
+        */
+       if (!head || !head->next) {
+               return;
+       }
+
+       map_sort_split(head, &a, &b);   /* Split into sublists */
+       map_sort(&a, cmp);              /* Traverse left */
+       map_sort(&b, cmp);              /* Traverse right */
+
+       /*
+        *      merge the two sorted lists together
+        */
+       *maps = map_sort_merge(a, b, cmp);
+}
+
 /** Process map which has exec as a src
  *
- * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so
- * has been broken out into it's own function.
+ * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections,
+ * and so has been broken out into it's own function.
  *
- * @param[out] out Where to write the VALUE_PAIR(s).
+ * @param[in,out] ctx to allocate new #VALUE_PAIR (s) in.
+ * @param[out] out Where to write the #VALUE_PAIR (s).
  * @param[in] request structure (used only for talloc).
- * @param[in] map the map. The LHS (dst) must be TMPL_TYPE_ATTR or TMPL_TYPE_LIST. The RHS (src) must be TMPL_TYPE_EXEC.
+ * @param[in] map the map. The LHS (dst) must be TMPL_TYPE_ATTR or TMPL_TYPE_LIST. The RHS (src)
+ *     must be TMPL_TYPE_EXEC.
  * @return -1 on failure, 0 on success.
  */
-static int map_exec_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map)
+static int map_exec_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map)
 {
        int result;
        char *expanded = NULL;
@@ -508,9 +680,10 @@ static int map_exec_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t c
         *      Automagically switch output type depending on our destination
         *      If dst is a list, then we create attributes from the output of the program
         *      if dst is an attribute, then we create an attribute of that type and then
-        *      call pairparsevalue on the output of the script.
+        *      call fr_pair_value_from_str on the output of the script.
         */
-       result = radius_exec_program(answer, sizeof(answer), (map->lhs->type == TMPL_TYPE_LIST) ? &output_pairs : NULL,
+       result = radius_exec_program(ctx, answer, sizeof(answer),
+                                    (map->lhs->type == TMPL_TYPE_LIST) ? &output_pairs : NULL,
                                     request, map->rhs->name, input_pairs ? *input_pairs : NULL,
                                     true, true, EXEC_TIMEOUT);
        talloc_free(expanded);
@@ -532,11 +705,12 @@ static int map_exec_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t c
        {
                VALUE_PAIR *vp;
 
-               vp = pairalloc(request, map->lhs->tmpl_da);
+               vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
                if (!vp) return -1;
                vp->op = map->op;
-               if (pairparsevalue(vp, answer, -1) < 0) {
-                       pairfree(&vp);
+               vp->tag = map->lhs->tmpl_tag;
+               if (fr_pair_value_from_str(vp, answer, -1) < 0) {
+                       fr_pair_list_free(&vp);
                        return -2;
                }
                *out = vp;
@@ -553,13 +727,16 @@ static int map_exec_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t c
 
 /** Convert a map to a VALUE_PAIR.
  *
- * @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 TMPL_TYPE_ATTR or TMPL_TYPE_LIST.
- * @param[in] ctx unused
- * @return 0 on success, -1 on failure
+ * @param[in,out] ctx to allocate #VALUE_PAIR (s) in.
+ * @param[out] out Where to write the #VALUE_PAIR (s), which may be NULL if not found
+ * @param[in] request The current request.
+ * @param[in] map the map. The LHS (dst) has to be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
+ * @param[in] uctx unused.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
  */
-int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx)
+int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, UNUSED void *uctx)
 {
        int rcode = 0;
        ssize_t len;
@@ -595,7 +772,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                }
                if (!from) return 0;
 
-               found = paircopy(request, *from);
+               found = fr_pair_list_copy(ctx, *from);
 
                /*
                 *      List to list copy is empty if the src list has no attributes.
@@ -622,7 +799,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                rad_assert(map->lhs->tmpl_da);  /* We need to know which attribute to create */
                rad_assert(map->rhs->tmpl_xlat != NULL);
 
-               new = pairalloc(request, map->lhs->tmpl_da);
+               new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
                if (!new) return -1;
 
                str = NULL;
@@ -640,13 +817,14 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                RDEBUG2("EXPAND %s", map->rhs->name);
                RDEBUG2("   --> %s", str);
 
-               rcode = pairparsevalue(new, str, -1);
+               rcode = fr_pair_value_from_str(new, str, -1);
                talloc_free(str);
                if (rcode < 0) {
-                       pairfree(&new);
+                       fr_pair_list_free(&new);
                        goto error;
                }
                new->op = map->op;
+               new->tag = map->lhs->tmpl_tag;
                *out = new;
                break;
 
@@ -654,7 +832,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
                rad_assert(map->lhs->tmpl_da);  /* We need to know which attribute to create */
 
-               new = pairalloc(request, map->lhs->tmpl_da);
+               new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
                if (!new) return -1;
 
                str = NULL;
@@ -664,13 +842,14 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                        goto error;
                }
 
-               rcode = pairparsevalue(new, str, -1);
+               rcode = fr_pair_value_from_str(new, str, -1);
                talloc_free(str);
                if (rcode < 0) {
-                       pairfree(&new);
+                       fr_pair_list_free(&new);
                        goto error;
                }
                new->op = map->op;
+               new->tag = map->lhs->tmpl_tag;
                *out = new;
                break;
 
@@ -678,14 +857,15 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
                rad_assert(map->lhs->tmpl_da);  /* We need to know which attribute to create */
 
-               new = pairalloc(request, map->lhs->tmpl_da);
+               new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
                if (!new) return -1;
 
-               if (pairparsevalue(new, map->rhs->name, -1) < 0) {
+               if (fr_pair_value_from_str(new, map->rhs->name, -1) < 0) {
                        rcode = 0;
                        goto error;
                }
                new->op = map->op;
+               new->tag = map->lhs->tmpl_tag;
                *out = new;
                break;
 
@@ -699,7 +879,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                /*
                 * @todo should log error, and return -1 for v3.1 (causes update to fail)
                 */
-               if (tmpl_copy_vps(request, &found, request, map->rhs) < 0) return 0;
+               if (tmpl_copy_vps(ctx, &found, request, map->rhs) < 0) return 0;
 
                vp = fr_cursor_init(&from, &found);
 
@@ -713,15 +893,15 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
 
                        (void) fr_cursor_init(&to, out);
                        for (; vp; vp = fr_cursor_next(&from)) {
-                               new = pairalloc(request, map->lhs->tmpl_da);
+                               new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
                                if (!new) return -1;
 
                                len = value_data_cast(new, &new->data, new->da->type, new->da,
                                                      vp->da->type, vp->da, &vp->data, vp->vp_length);
                                if (len < 0) {
                                        REDEBUG("Attribute conversion failed: %s", fr_strerror());
-                                       pairfree(&found);
-                                       pairfree(&new);
+                                       fr_pair_list_free(&found);
+                                       fr_pair_list_free(&new);
                                        return -1;
                                }
 
@@ -734,6 +914,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                                }
 
                                new->op = map->op;
+                               new->tag = map->lhs->tmpl_tag;
                                fr_cursor_insert(&to, new);
                        }
                        return 0;
@@ -746,6 +927,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                for (; vp; vp = fr_cursor_next(&from)) {
                        vp->da = map->lhs->tmpl_da;
                        vp->op = map->op;
+                       vp->tag = map->lhs->tmpl_tag;
                }
                *out = found;
        }
@@ -756,7 +938,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
                rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
                rad_assert(map->lhs->tmpl_da->type == map->rhs->tmpl_data_type);
 
-               new = pairalloc(request, map->lhs->tmpl_da);
+               new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
                if (!new) return -1;
 
                len = value_data_copy(new, &new->data, new->da->type, &map->rhs->tmpl_data_value,
@@ -765,6 +947,7 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
 
                new->vp_length = len;
                new->op = map->op;
+               new->tag = map->lhs->tmpl_tag;
                *out = new;
 
                VERIFY_MAP(map);
@@ -778,13 +961,13 @@ int map_to_vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, U
         *      exec string is xlat expanded and arguments are shell escaped.
         */
        case TMPL_TYPE_EXEC:
-               return map_exec_to_vp(out, request, map);
+               return map_exec_to_vp(ctx, out, request, map);
 
        default:
                rad_assert(0);  /* Should have been caught at parse time */
 
        error:
-               pairfree(&vp);
+               fr_pair_list_free(&vp);
                return rcode;
        }
 
@@ -802,9 +985,9 @@ do {\
        }\
 } while (0)
 
-/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
+/** Convert vp_map_t to VALUE_PAIR(s) and add them to a REQUEST.
  *
- * Takes a single value_pair_map_t, resolves request and list identifiers
+ * Takes a single vp_map_t, resolves request and list identifiers
  * to pointers in the current request, then attempts to retrieve module
  * specific value(s) using callback, and adds the resulting values to the
  * correct request/list.
@@ -816,7 +999,7 @@ do {\
  * @param ctx to be passed to func.
  * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
  */
-int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_getvalue_t func, void *ctx)
+int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
 {
        int rcode = 0;
        int num;
@@ -826,8 +1009,8 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
        TALLOC_CTX *parent;
        vp_cursor_t dst_list, src_list;
 
-       value_pair_map_t        exp_map;
-       value_pair_tmpl_t       exp_lhs;
+       vp_map_t        exp_map;
+       vp_tmpl_t       exp_lhs;
 
        VERIFY_MAP(map);
        rad_assert(map->lhs != NULL);
@@ -908,7 +1091,10 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
         */
        if (((map->lhs->tmpl_list == PAIR_LIST_COA) ||
             (map->lhs->tmpl_list == PAIR_LIST_DM)) && !request->coa) {
-               request_alloc_coa(context);
+               if (!request_alloc_coa(context)) {
+                       REDEBUG("Failed to create a CoA/Disconnect Request message");
+                       return -2;
+               }
                context->coa->proxy->code = (map->lhs->tmpl_list == PAIR_LIST_COA) ?
                                            PW_CODE_COA_REQUEST :
                                            PW_CODE_DISCONNECT_REQUEST;
@@ -916,7 +1102,8 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
 
        list = radius_list(context, map->lhs->tmpl_list);
        if (!list) {
-               REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->rhs->name, map->lhs->name);
+               REDEBUG("Mapping \"%.*s\" -> \"%.*s\" invalid in this context",
+                       (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
 
                return -2;
        }
@@ -927,11 +1114,11 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
        /*
         *      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
+        *      0 to signify success. It may return "success", but still have no
         *      VPs to work with.
         */
        if (map->rhs->type != TMPL_TYPE_NULL) {
-               rcode = func(&head, request, map, ctx);
+               rcode = func(parent, &head, request, map, ctx);
                if (rcode < 0) {
                        rad_assert(!head);
                        return rcode;
@@ -941,19 +1128,18 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                        return rcode;
                }
        } else {
-               if (debug_flag) map_debug_log(request, map, NULL);
+               if (rad_debug_lvl) map_debug_log(request, map, NULL);
        }
 
        /*
-        *      Reparent the VPs (func may return multiple)
+        *      Print the VPs
         */
        for (vp = fr_cursor_init(&src_list, &head);
             vp;
             vp = fr_cursor_next(&src_list)) {
                VERIFY_VP(vp);
 
-               if (debug_flag) map_debug_log(request, map, vp);
-               pairsteal(parent, vp);
+               if (rad_debug_lvl) map_debug_log(request, map, vp);
        }
 
        /*
@@ -966,7 +1152,7 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                        rad_assert(!head);
 
                        /* Clear the entire dst list */
-                       pairfree(list);
+                       fr_pair_list_free(list);
 
                        if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
                                context->username = NULL;
@@ -976,24 +1162,20 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
 
                case T_OP_SET:
                        if (map->rhs->type == TMPL_TYPE_LIST) {
-                               pairfree(list);
+                               fr_pair_list_free(list);
                                *list = head;
                                head = NULL;
                        } else {
                case T_OP_EQ:
                                rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
-                               pairmove(parent, list, &head);
-                               pairfree(&head);
-                       }
-                       goto finish;
-
                case T_OP_ADD:
-                       pairadd(list, head);
-                       head = NULL;
+                               fr_pair_list_move(parent, list, &head);
+                               fr_pair_list_free(&head);
+                       }
                        goto finish;
 
                default:
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return -1;
                }
        }
@@ -1033,14 +1215,14 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                 *      Wildcard: delete all of the matching ones, based on tag.
                 */
                if (map->lhs->tmpl_num == NUM_ANY) {
-                       pairdelete(list, map->lhs->tmpl_da->attr, map->lhs->tmpl_da->vendor, map->lhs->tmpl_tag);
+                       fr_pair_delete_by_num(list, map->lhs->tmpl_da->attr, map->lhs->tmpl_da->vendor, map->lhs->tmpl_tag);
                        dst = NULL;
                /*
                 *      We've found the Nth one.  Delete it, and only it.
                 */
                } else {
                        dst = fr_cursor_remove(&dst_list);
-                       pairfree(&dst);
+                       fr_pair_list_free(&dst);
                }
 
                /*
@@ -1062,7 +1244,7 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
        case T_OP_SUB:
                /* We didn't find any attributes earlier */
                if (!dst) {
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return 0;
                }
 
@@ -1077,11 +1259,11 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                                rcode = radius_compare_vps(request, vp, dst);
                                if (rcode == 0) {
                                        dst = fr_cursor_remove(&dst_list);
-                                       pairfree(&dst);
+                                       fr_pair_list_free(&dst);
                                        found = true;
                                }
                        }
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        if (!found) return 0;
                        goto finish;
                }
@@ -1099,12 +1281,12 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                                rcode = radius_compare_vps(request, vp, dst);
                                if (rcode == 0) {
                                        dst = fr_cursor_remove(&dst_list);
-                                       pairfree(&dst);
+                                       fr_pair_list_free(&dst);
                                        found = true;
                                }
                        }
                }
-               pairfree(&head);
+               fr_pair_list_free(&head);
                if (!found) return 0;
                goto finish;
        }
@@ -1127,7 +1309,7 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
        case T_OP_EQ:
                if (dst) {
                        RDEBUG3("Refusing to overwrite (use :=)");
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return 0;
                }
 
@@ -1135,7 +1317,7 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                fr_cursor_first(&src_list);
                fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
                /* Free any we didn't insert */
-               pairfree(&head);
+               fr_pair_list_free(&head);
                break;
 
        /*
@@ -1145,13 +1327,14 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                /* 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);
+                       dst = fr_cursor_replace(&dst_list, fr_cursor_remove(&src_list));
+                       fr_pair_list_free(&dst);
+               } else {
+                       fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
                }
-               fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
                /* Free any we didn't insert */
-               pairfree(&head);
+               fr_pair_list_free(&head);
                break;
 
        /*
@@ -1159,7 +1342,7 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
         */
        case T_OP_ADD:
                /* Insert all the instances! (if multiple) */
-               pairadd(list, head);
+               fr_pair_add(list, head);
                head = NULL;
                break;
 
@@ -1196,7 +1379,7 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                        replace:
                                dst = fr_cursor_remove(&dst_list);
                                DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
-                               pairfree(&dst);
+                               fr_pair_list_free(&dst);
                                fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
                                found = true;
                                continue;
@@ -1210,11 +1393,11 @@ int map_to_request(REQUEST *request, value_pair_map_t const *map, radius_map_get
                                goto replace;
 
                        default:
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
                                return -1;
                        }
                }
-               pairfree(&head);
+               fr_pair_list_free(&head);
                if (!found) return 0;
 
                break;
@@ -1267,7 +1450,7 @@ finish:
  * @param map to check.
  * @return true if the map resolves to a request and list else false.
  */
-bool map_dst_valid(REQUEST *request, value_pair_map_t const *map)
+bool map_dst_valid(REQUEST *request, vp_map_t const *map)
 {
        REQUEST *context = request;
 
@@ -1286,7 +1469,7 @@ bool map_dst_valid(REQUEST *request, value_pair_map_t const *map)
  * @param[in] map to print
  * @return the size of the string printed
  */
-size_t map_prints(char *buffer, size_t bufsize, value_pair_map_t const *map)
+size_t map_prints(char *buffer, size_t bufsize, vp_map_t const *map)
 {
        size_t len;
        DICT_ATTR const *da = NULL;
@@ -1336,7 +1519,7 @@ size_t map_prints(char *buffer, size_t bufsize, value_pair_map_t const *map)
 /*
  *     Debug print a map / VP
  */
-void map_debug_log(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR const *vp)
+void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
 {
        char *value;
        char buffer[1024];
@@ -1353,42 +1536,68 @@ void map_debug_log(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR con
         */
        default:
        case TMPL_TYPE_LITERAL:
-               vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+               vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
                value = buffer;
                break;
 
        case TMPL_TYPE_XLAT:
        case TMPL_TYPE_XLAT_STRUCT:
-               vp_prints_value(buffer, sizeof(buffer), vp, '"');
+               vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
                value = buffer;
                break;
 
        case TMPL_TYPE_DATA:
-               vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+               vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
                value = buffer;
                break;
 
        /*
-        *      Just printing the value doesn't make sense, but we still
-        *      want to know what it was...
+        *      For the lists, we can't use the original name, and have to
+        *      rebuild it using tmpl_prints, for each attribute we're
+        *      copying.
         */
        case TMPL_TYPE_LIST:
-               vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+       {
+               char            attr[256];
+               char            quote = '\0';
+               vp_tmpl_t       vpt;
+               /*
+                *      Fudge a temporary tmpl that describes the attribute we're copying
+                *      this is a combination of the original list tmpl, and values from
+                *      the VALUE_PAIR. This way, we get tag info included.
+                */
+               memcpy(&vpt, map->rhs, sizeof(vpt));
+               vpt.tmpl_da = vp->da;
+               vpt.tmpl_tag = vp->tag;
+               vpt.type = TMPL_TYPE_ATTR;
 
-               if (map->rhs->tmpl_request == REQUEST_OUTER) {
-                       value = talloc_typed_asprintf(request, "&outer.%s:%s -> %s",
-                                                     fr_int2str(pair_lists, map->rhs->tmpl_list, "<INVALID>"),
-                                                     vp->da->name, buffer);
-               } else {
-                       value = talloc_typed_asprintf(request, "&%s:%s -> %s",
-                                                     fr_int2str(pair_lists, map->rhs->tmpl_list, "<INVALID>"),
-                                                     vp->da->name, buffer);
-               }
+               /*
+                *      Not appropriate to use map->rhs->quote here, as that's the quoting
+                *      around the list ref. The attribute value has no quoting, so we choose
+                *      the quoting based on the data type, and whether it's printable.
+                */
+               if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
+                                                                        vp->vp_length) ? '\'' : '"';
+               vp_prints_value(buffer, sizeof(buffer), vp, quote);
+               tmpl_prints(attr, sizeof(attr), &vpt, vp->da);
+               value = talloc_typed_asprintf(request, "%s -> %s", attr, buffer);
+       }
                break;
 
        case TMPL_TYPE_ATTR:
-               vp_prints_value(buffer, sizeof(buffer), vp, '\'');
-               value = talloc_typed_asprintf(request, "&%s -> %s", map->rhs->tmpl_da->name, buffer);
+       {
+               char quote = '\0';
+
+               /*
+                *      Not appropriate to use map->rhs->quote here, as that's the quoting
+                *      around the attr ref. The attribute value has no quoting, so we choose
+                *      the quoting based on the data type, and whether it's printable.
+                */
+               if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
+                                                                        vp->vp_length) ? '\'' : '"';
+               vp_prints_value(buffer, sizeof(buffer), vp, quote);
+               value = talloc_typed_asprintf(request, "%.*s -> %s", (int)map->rhs->len, map->rhs->name, buffer);
+       }
                break;
 
        case TMPL_TYPE_NULL:
@@ -1399,7 +1608,7 @@ void map_debug_log(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR con
 
        switch (map->lhs->type) {
        case TMPL_TYPE_LIST:
-               RDEBUG("%s%s %s %s", map->lhs->name, vp ? vp->da->name : "",
+               RDEBUG("%.*s:%s %s %s", (int)map->lhs->len, map->lhs->name, vp ? vp->da->name : "",
                       fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
                break;
 
index c77bf1b..00324d6 100644 (file)
@@ -68,14 +68,13 @@ typedef struct {
        enum {
                GROUPTYPE_SIMPLE = 0,
                GROUPTYPE_REDUNDANT,
-               GROUPTYPE_APPEND,
                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 */
+       vp_map_t        *map;           /* update */
+       vp_tmpl_t       *vpt;           /* switch */
        fr_cond_t               *cond;          /* if/elsif */
        bool                    done_pass2;
 } modgroup;
@@ -322,7 +321,7 @@ static rlm_rcode_t CC_HINT(nonnull) call_modsingle(rlm_components_t component, m
        return request->rcode;
 }
 
-static int default_component_results[RLM_COMPONENT_COUNT] = {
+static int default_component_results[MOD_COUNT] = {
        RLM_MODULE_REJECT,      /* AUTH */
        RLM_MODULE_NOTFOUND,    /* AUTZ */
        RLM_MODULE_NOOP,        /* PREACCT */
@@ -381,14 +380,14 @@ typedef struct modcall_stack_entry_t {
 
 
 static bool modcall_recurse(REQUEST *request, rlm_components_t component, int depth,
-                           modcall_stack_entry_t *entry);
+                           modcall_stack_entry_t *entry, bool do_next_sibling);
 
 /*
  *     Call a child of a block.
  */
 static void modcall_child(REQUEST *request, rlm_components_t component, int depth,
                          modcall_stack_entry_t *entry, modcallable *c,
-                         rlm_rcode_t *result)
+                         rlm_rcode_t *result, bool do_next_sibling)
 {
        modcall_stack_entry_t *next;
 
@@ -407,7 +406,7 @@ static void modcall_child(REQUEST *request, rlm_components_t component, int dept
        next->unwind = 0;
 
        if (!modcall_recurse(request, component,
-                            depth, next)) {
+                            depth, next, do_next_sibling)) {
                *result = RLM_MODULE_FAIL;
                 return;
        }
@@ -429,7 +428,7 @@ static void modcall_child(REQUEST *request, rlm_components_t component, int dept
  *     Interpret the various types of blocks.
  */
 static bool modcall_recurse(REQUEST *request, rlm_components_t component, int depth,
-                           modcall_stack_entry_t *entry)
+                           modcall_stack_entry_t *entry, bool do_next_sibling)
 {
        bool if_taken, was_if;
        modcallable *c;
@@ -585,7 +584,7 @@ redo:
        if (c->type == MOD_UPDATE) {
                int rcode;
                modgroup *g = mod_callabletogroup(c);
-               value_pair_map_t *map;
+               vp_map_t *map;
 
                MOD_LOG_OPEN_BRACE;
                RINDENT();
@@ -660,7 +659,7 @@ redo:
                     vp != NULL;
                     vp = fr_cursor_next(&copy)) {
 #ifndef NDEBUG
-                       if (fr_debug_flag >= 2) {
+                       if (fr_debug_lvl >= 2) {
                                char buffer[1024];
 
                                vp_prints_value(buffer, sizeof(buffer), vp, '"');
@@ -683,15 +682,17 @@ redo:
                        next->priority = 0;
                        next->unwind = 0;
 
-                       if (!modcall_recurse(request, component, depth + 1, next)) {
+                       if (!modcall_recurse(request, component, depth + 1, next, true)) {
                                break;
                        }
 
                        /*
-                        *      We've unwound to the enclosing
-                        *      "foreach".  Stop the unwinding.
+                        *      We've been asked to unwind to the
+                        *      enclosing "foreach".  We're here, so
+                        *      we can stop unwinding.
                         */
-                       if (next->unwind == MOD_FOREACH) {
+                       if (next->unwind == MOD_BREAK) {
+                               entry->unwind = 0;
                                break;
                        }
 
@@ -709,7 +710,7 @@ redo:
                 *      If we don't remove the request data, something could call
                 *      the xlat outside of a foreach loop and trigger a segv.
                 */
-               pairfree(&vps);
+               fr_pair_list_free(&vps);
                request_data_get(request, (void *)radius_get_vp, foreach_depth);
 
                rad_assert(next != NULL);
@@ -771,6 +772,11 @@ redo:
                 *      MOD_GROUP.
                 */
                if (!g->children) {
+                       if (c->type == MOD_CASE) {
+                               result = RLM_MODULE_NOOP;
+                               goto calculate_result;
+                       }
+
                        RDEBUG2("%s { ... } # empty sub-section is ignored", c->name);
                        goto next_sibling;
                }
@@ -778,7 +784,7 @@ redo:
                MOD_LOG_OPEN_BRACE;
                modcall_child(request, component,
                              depth + 1, entry, g->children,
-                             &result);
+                             &result, true);
                MOD_LOG_CLOSE_BRACE;
                goto calculate_result;
        } /* MOD_GROUP */
@@ -789,8 +795,8 @@ redo:
                modgroup *g, *h;
                fr_cond_t cond;
                value_data_t data;
-               value_pair_map_t map;
-               value_pair_tmpl_t vpt;
+               vp_map_t map;
+               vp_tmpl_t vpt;
 
                MOD_LOG_OPEN_BRACE;
 
@@ -838,10 +844,12 @@ redo:
                    (g->vpt->type == TMPL_TYPE_XLAT) ||
                    (g->vpt->type == TMPL_TYPE_EXEC)) {
                        char *p;
+                       ssize_t len;
 
-                       if (tmpl_aexpand(request, &p, request, g->vpt, NULL, NULL) < 0) goto find_null_case;
+                       len = tmpl_aexpand(request, &p, request, g->vpt, NULL, NULL);
+                       if (len < 0) goto find_null_case;
                        data.strvalue = p;
-                       tmpl_init(&vpt, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       tmpl_init(&vpt, TMPL_TYPE_LITERAL, data.strvalue, len);
                }
 
                /*
@@ -911,7 +919,7 @@ redo:
 
        do_null_case:
                talloc_free(data.ptr);
-               modcall_child(request, component, depth + 1, entry, found, &result);
+               modcall_child(request, component, depth + 1, entry, found, &result, true);
                MOD_LOG_CLOSE_BRACE;
                goto calculate_result;
        } /* MOD_SWITCH */
@@ -940,12 +948,10 @@ redo:
                        }
                }
 
-               MOD_LOG_OPEN_BRACE;
-
                if (c->type == MOD_LOAD_BALANCE) {
                        modcall_child(request, component,
                                      depth + 1, entry, found,
-                                     &result);
+                                     &result, false);
 
                } else {
                        this = found;
@@ -953,7 +959,7 @@ redo:
                        do {
                                modcall_child(request, component,
                                              depth + 1, entry, this,
-                                             &result);
+                                             &result, false);
                                if (this->actions[result] == MOD_ACTION_RETURN) {
                                        priority = -1;
                                        break;
@@ -1004,7 +1010,7 @@ redo:
                        radius_xlat(buffer, sizeof(buffer), request, mx->xlat_name, NULL, NULL);
                } else {
                        RDEBUG("`%s`", mx->xlat_name);
-                       radius_exec_program(NULL, 0, NULL, request, mx->xlat_name, request->packet->vps,
+                       radius_exec_program(request, NULL, 0, NULL, request, mx->xlat_name, request->packet->vps,
                                            false, true, EXEC_TIMEOUT);
                }
 
@@ -1076,7 +1082,6 @@ calculate_result:
         */
        if (entry->unwind == MOD_BREAK) {
                RDEBUG2("# unwind to enclosing foreach");
-               entry->unwind = 0;
                goto finish;
        }
 
@@ -1085,9 +1090,11 @@ calculate_result:
        }
 
 next_sibling:
-       entry->c = entry->c->next;
+       if (do_next_sibling) {
+               entry->c = entry->c->next;
 
-       if (entry->c) goto redo;
+               if (entry->c) goto redo;
+       }
 
 finish:
        /*
@@ -1120,7 +1127,7 @@ int modcall(rlm_components_t component, modcallable *c, REQUEST *request)
        /*
         *      Call the main handler.
         */
-       if (!modcall_recurse(request, component, 0, &stack[0])) {
+       if (!modcall_recurse(request, component, 0, &stack[0], true)) {
                return RLM_MODULE_FAIL;
        }
 
@@ -1182,10 +1189,10 @@ static void dump_tree(rlm_components_t comp, modcallable *c)
 #endif
 
 /* These are the default actions. For each component, the group{} block
- * behaves like the code from the old module_*() function. redundant{} and
- * append{} are based on my guesses of what they will be used for. --Pac. */
+ * behaves like the code from the old module_*() function. redundant{}
+ * are based on my guesses of what they will be used for. --Pac. */
 static const int
-defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
+defaultactions[MOD_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
 {
        /* authenticate */
        {
@@ -1212,18 +1219,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* authorize */
@@ -1251,18 +1246,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* preacct */
@@ -1290,18 +1273,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* accounting */
@@ -1329,18 +1300,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        1,                      /* notfound */
                        2,                      /* noop     */
                        4                       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* checksimul */
@@ -1368,18 +1327,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       MOD_ACTION_RETURN,      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* pre-proxy */
@@ -1407,18 +1354,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* post-proxy */
@@ -1446,18 +1381,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* post-auth */
@@ -1485,18 +1408,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        }
 #ifdef WITH_COA
@@ -1526,18 +1437,6 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        },
        /* send-coa */
@@ -1565,30 +1464,46 @@ defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
                        MOD_ACTION_RETURN,      /* notfound */
                        MOD_ACTION_RETURN,      /* noop     */
                        MOD_ACTION_RETURN       /* updated  */
-               },
-               /* append */
-               {
-                       MOD_ACTION_RETURN,      /* reject   */
-                       1,                      /* fail     */
-                       MOD_ACTION_RETURN,      /* ok       */
-                       MOD_ACTION_RETURN,      /* handled  */
-                       MOD_ACTION_RETURN,      /* invalid  */
-                       MOD_ACTION_RETURN,      /* userlock */
-                       2,                      /* notfound */
-                       MOD_ACTION_RETURN,      /* noop     */
-                       MOD_ACTION_RETURN       /* updated  */
                }
        }
 #endif
 };
 
+static const int authtype_actions[GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
+{
+       /* group */
+       {
+               MOD_ACTION_RETURN,      /* reject   */
+               MOD_ACTION_RETURN,      /* fail     */
+               4,                      /* ok       */
+               MOD_ACTION_RETURN,      /* handled  */
+               MOD_ACTION_RETURN,      /* invalid  */
+               MOD_ACTION_RETURN,      /* userlock */
+               1,                      /* notfound */
+               2,                      /* noop     */
+               3                       /* updated  */
+       },
+       /* redundant */
+       {
+               MOD_ACTION_RETURN,      /* reject   */
+               1,                      /* fail     */
+               MOD_ACTION_RETURN,      /* ok       */
+               MOD_ACTION_RETURN,      /* handled  */
+               MOD_ACTION_RETURN,      /* invalid  */
+               MOD_ACTION_RETURN,      /* userlock */
+               MOD_ACTION_RETURN,      /* notfound */
+               MOD_ACTION_RETURN,      /* noop     */
+               MOD_ACTION_RETURN       /* updated  */
+       }
+};
+
 /** Validate and fixup a map that's part of an update section.
  *
  * @param map to validate.
  * @param ctx data to pass to fixup function (currently unused).
  * @return 0 if valid else -1.
  */
-int modcall_fixup_update(value_pair_map_t *map, UNUSED void *ctx)
+int modcall_fixup_update(vp_map_t *map, UNUSED void *ctx)
 {
        CONF_PAIR *cp = cf_item_to_pair(map->ci);
 
@@ -1620,7 +1535,7 @@ int modcall_fixup_update(value_pair_map_t *map, UNUSED void *ctx)
                             cf_pair_filename(cp), cf_pair_lineno(cp));
                }
 
-               tmpl_free(&map->rhs);
+               TALLOC_FREE(map->rhs);
 
                map->rhs = tmpl_alloc(map, TMPL_TYPE_NULL, NULL, 0);
        }
@@ -1641,22 +1556,11 @@ int modcall_fixup_update(value_pair_map_t *map, UNUSED void *ctx)
        /*
         *      Depending on the attribute type, some operators are disallowed.
         */
-       if (map->lhs->type == TMPL_TYPE_ATTR) {
-               switch (map->op) {
-               default:
-                       cf_log_err(map->ci, "Invalid operator for attribute");
-                       return -1;
-
-               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;
-               }
+       if ((map->lhs->type == TMPL_TYPE_ATTR) && (!fr_assignment_op[map->op] && !fr_equality_op[map->op])) {
+               cf_log_err(map->ci, "Invalid operator \"%s\" in update section.  "
+                          "Only assignment or filter operators are allowed",
+                          fr_int2str(fr_tokens, map->op, "<INVALID>"));
+               return -1;
        }
 
        if (map->lhs->type == TMPL_TYPE_LIST) {
@@ -1696,7 +1600,7 @@ int modcall_fixup_update(value_pair_map_t *map, UNUSED void *ctx)
 
                case T_OP_SET:
                        if (map->rhs->type == TMPL_TYPE_EXEC) {
-                               WARN("%s[%d] Please change ':=' to '=' for list assignment",
+                               WARN("%s[%d]: Please change ':=' to '=' for list assignment",
                                     cf_pair_filename(cp), cf_pair_lineno(cp));
                        }
 
@@ -1742,8 +1646,26 @@ int modcall_fixup_update(value_pair_map_t *map, UNUSED void *ctx)
                    (map->lhs->tmpl_da->type == PW_TYPE_STRING) &&
                    (cf_pair_value_type(cp) == T_SINGLE_QUOTED_STRING)) {
                        tmpl_cast_in_place_str(map->rhs);
+
                } else {
-                       if (!tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da)) {
+                       /*
+                        *      RHS is hex, try to parse it as
+                        *      type-specific data.
+                        */
+                       if (map->lhs->auto_converted &&
+                           (map->rhs->name[0] == '0') && (map->rhs->name[1] == 'x') &&
+                           (map->rhs->len > 2) && ((map->rhs->len & 0x01) == 0)) {
+                               vp_tmpl_t *vpt = map->rhs;
+                               map->rhs = NULL;
+
+                               if (!map_cast_from_hex(map, T_BARE_WORD, vpt->name)) {
+                                       map->rhs = vpt;
+                                       cf_log_err(map->ci, "%s", fr_strerror());
+                                       return -1;
+                               }
+                               talloc_free(vpt);
+
+                       } else if (tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) {
                                cf_log_err(map->ci, "%s", fr_strerror());
                                return -1;
                        }
@@ -1780,7 +1702,7 @@ static modcallable *do_compile_modupdate(modcallable *parent, rlm_components_t c
        modgroup *g;
        modcallable *csingle;
 
-       value_pair_map_t *head;
+       vp_map_t *head;
 
        /*
         *      This looks at cs->name2 to determine which list to update
@@ -1827,7 +1749,7 @@ static modcallable *do_compile_modswitch (modcallable *parent, rlm_components_t
        modcallable *csingle;
        modgroup *g;
        ssize_t slen;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        name2 = cf_section_name2(cs);
        if (!name2) {
@@ -1847,7 +1769,7 @@ static modcallable *do_compile_modswitch (modcallable *parent, rlm_components_t
         *      will fix it up.
         */
        type = cf_section_name2_type(cs);
-       slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+       slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
        if ((slen < 0) && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
                char *spaces, *text;
 
@@ -1935,7 +1857,7 @@ static modcallable *do_compile_modcase(modcallable *parent, rlm_components_t com
        char const *name2;
        modcallable *csingle;
        modgroup *g;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        if (!parent || (parent->type != MOD_SWITCH)) {
                cf_log_err_cs(cs, "\"case\" statements may only appear within a \"switch\" section");
@@ -1953,7 +1875,7 @@ static modcallable *do_compile_modcase(modcallable *parent, rlm_components_t com
 
                type = cf_section_name2_type(cs);
 
-               slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+               slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
                if ((slen < 0) && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
                        char *spaces, *text;
 
@@ -2022,7 +1944,7 @@ static modcallable *do_compile_modforeach(modcallable *parent,
        modcallable *csingle;
        modgroup *g;
        ssize_t slen;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        name2 = cf_section_name2(cs);
        if (!name2) {
@@ -2043,7 +1965,7 @@ static modcallable *do_compile_modforeach(modcallable *parent,
         *      will fix it up.
         */
        type = cf_section_name2_type(cs);
-       slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+       slen = tmpl_afrom_str(cs, &vpt, name2, strlen(name2), type, REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
        if ((slen < 0) && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
                char *spaces, *text;
 
@@ -2229,6 +2151,96 @@ static int all_children_are_modules(CONF_SECTION *cs, char const *name)
        return 1;
 }
 
+/** Load a named module from "instantiate" or "policy".
+ *
+ * If it's "foo.method", look for "foo", and return "method" as the method
+ * we wish to use, instead of the input component.
+ *
+ * @param[out] pcomponent Where to write the method we found, if any.  If no method is specified
+ *     will be set to MOD_COUNT.
+ * @param[in] real_name Complete name string e.g. foo.authorize.
+ * @param[in] virtual_name Virtual module name e.g. foo.
+ * @param[in] method_name Method override (may be NULL) or the method name e.g. authorize.
+ * @return the CONF_SECTION specifying the virtual module.
+ */
+static CONF_SECTION *virtual_module_find_cs(rlm_components_t *pcomponent,
+                                           char const *real_name, char const *virtual_name, char const *method_name)
+{
+       CONF_SECTION *cs, *subcs;
+       rlm_components_t method = *pcomponent;
+       char buffer[256];
+
+       /*
+        *      Turn the method name into a method enum.
+        */
+       if (method_name) {
+               rlm_components_t i;
+
+               for (i = MOD_AUTHENTICATE; i < MOD_COUNT; i++) {
+                       if (strcmp(comp2str[i], method_name) == 0) break;
+               }
+
+               if (i != MOD_COUNT) {
+                       method = i;
+               } else {
+                       method_name = NULL;
+                       virtual_name = real_name;
+               }
+       }
+
+       /*
+        *      Look for "foo" in the "instantiate" section.  If we
+        *      find it, AND there's no method name, we've found the
+        *      right thing.
+        *
+        *      Return it to the caller, with the updated method.
+        */
+       cs = cf_section_find("instantiate");
+       if (cs) {
+               /*
+                *      Found "foo".  Load it as "foo", or "foo.method".
+                */
+               subcs = cf_section_sub_find_name2(cs, NULL, virtual_name);
+               if (subcs) {
+                       *pcomponent = method;
+                       return subcs;
+               }
+       }
+
+       /*
+        *      Look for it in "policy".
+        *
+        *      If there's no policy section, we can't do anything else.
+        */
+       cs = cf_section_find("policy");
+       if (!cs) return NULL;
+
+       /*
+        *      "foo.authorize" means "load policy "foo" as method "authorize".
+        *
+        *      And bail out if there's no policy "foo".
+        */
+       if (method_name) {
+               subcs = cf_section_sub_find_name2(cs, NULL, virtual_name);
+               if (subcs) *pcomponent = method;
+
+               return subcs;
+       }
+
+       /*
+        *      "foo" means "look for foo.component" first, to allow
+        *      method overrides.  If that's not found, just look for
+        *      a policy "foo".
+        *
+        */
+       snprintf(buffer, sizeof(buffer), "%s.%s",
+                virtual_name, comp2str[method]);
+       subcs = cf_section_sub_find_name2(cs, NULL, buffer);
+       if (subcs) return subcs;
+
+       return cf_section_sub_find_name2(cs, NULL, virtual_name);
+}
+
 
 /*
  *     Compile one entry of a module call.
@@ -2278,12 +2290,6 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                                                   GROUPTYPE_REDUNDANT,
                                                   grouptype, MOD_GROUP);
 
-               } else if (strcmp(modrefname, "append") == 0) {
-                       *modname = name2;
-                       return do_compile_modgroup(parent, component, cs,
-                                                  GROUPTYPE_APPEND,
-                                                  grouptype, MOD_GROUP);
-
                } else if (strcmp(modrefname, "load-balance") == 0) {
                        *modname = name2;
 
@@ -2511,31 +2517,21 @@ static modcallable *do_compile_modsingle(modcallable *parent,
         *      policy { ... name { .. } .. }
         *      policy { ... name.method { .. } .. }
         *
-        *      The "instantiate" virtual modules are identical to the
-        *      policies at this point.  We should probably get rid of
-        *      the "instantiate" ones, as they're duplicate and
-        *      confusing.
+        *      The only difference between things in "instantiate"
+        *      and "policy" is that "instantiate" will cause modules
+        *      to be instantiated in a particular order.
         */
        subcs = NULL;
-       cs = cf_section_find("instantiate");
-       if (cs) subcs = cf_section_sub_find_name2(cs, NULL,
-                                                 modrefname);
-       if (!subcs &&
-           (cs = cf_section_find("policy")) != NULL) {
+       p = strrchr(modrefname, '.');
+       if (!p) {
+               subcs = virtual_module_find_cs(&method, modrefname, modrefname, NULL);
+       } else {
                char buffer[256];
 
-               snprintf(buffer, sizeof(buffer), "%s.%s",
-                        modrefname, comp2str[component]);
+               strlcpy(buffer, modrefname, sizeof(buffer));
+               buffer[p - modrefname] = '\0';
 
-               /*
-                *      Prefer name.section, then name.
-                */
-               subcs = cf_section_sub_find_name2(cs, NULL,
-                                                         buffer);
-               if (!subcs) {
-                       subcs = cf_section_sub_find_name2(cs, NULL,
-                                                         modrefname);
-               }
+               subcs = virtual_module_find_cs(&method, modrefname, buffer, buffer + (p - modrefname) + 1);
        }
 
        /*
@@ -2573,7 +2569,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                 */
                if (cf_section_name2(subcs)) {
                        csingle = do_compile_modsingle(parent,
-                                                      component,
+                                                      method,
                                                       cf_section_to_item(subcs),
                                                       grouptype,
                                                       modname);
@@ -2588,7 +2584,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                         *      group foo { ...
                         */
                        csingle = do_compile_modgroup(parent,
-                                                     component,
+                                                     method,
                                                      subcs,
                                                      GROUPTYPE_SIMPLE,
                                                      grouptype, MOD_GROUP);
@@ -2626,63 +2622,17 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                 *      modules belongs in raddb/mods-available/,
                 *      which isn't loaded into the "modules" section.
                 */
-               if (cf_section_sub_find_name2(modules, NULL, realname)) {
-                       this = find_module_instance(modules, realname, true);
-                       if (this) goto allocate_csingle;
-
-                       /*
-                        *
-                        */
-                       if (realname != modrefname) {
-                               return NULL;
-                       }
-
-               } else {
-                       /*
-                        *      We were asked to MAYBE load it and it
-                        *      doesn't exist.  Return a soft error.
-                        */
-                       if (realname != modrefname) {
-                               *modname = modrefname;
-                               return NULL;
-                       }
-               }
-       }
-
-       /*
-        *      No module found by that name.  Maybe we're calling
-        *      module.method
-        */
-       p = strrchr(modrefname, '.');
-       if (p) {
-               rlm_components_t i;
-               p++;
+               this = module_instantiate_method(modules, realname, &method);
+               if (this) goto allocate_csingle;
 
                /*
-                *      Find the component.
+                *      We were asked to MAYBE load it and it
+                *      doesn't exist.  Return a soft error.
                 */
-               for (i = RLM_COMPONENT_AUTH;
-                    i < RLM_COMPONENT_COUNT;
-                    i++) {
-                       if (strcmp(p, comp2str[i]) == 0) {
-                               char buffer[256];
-
-                               strlcpy(buffer, modrefname, sizeof(buffer));
-                               buffer[p - modrefname - 1] = '\0';
-                               component = i;
-
-                               this = find_module_instance(modules, buffer, true);
-                               if (this) {
-                                       method = i;
-                                       goto allocate_csingle;
-                               }
-                       }
+               if (realname != modrefname) {
+                       *modname = modrefname;
+                       return NULL;
                }
-
-               /*
-                *      FIXME: check for "module", and give error "no
-                *      such component" when we don't find the method.
-                */
        }
 
        /*
@@ -2715,11 +2665,11 @@ allocate_csingle:
        csingle = mod_singletocallable(single);
        csingle->parent = parent;
        csingle->next = NULL;
-       if (!parent || (component != RLM_COMPONENT_AUTH)) {
+       if (!parent || (component != MOD_AUTHENTICATE)) {
                memcpy(csingle->actions, defaultactions[component][grouptype],
                       sizeof csingle->actions);
        } else { /* inside Auth-Type has different rules */
-               memcpy(csingle->actions, defaultactions[RLM_COMPONENT_AUTZ][grouptype],
+               memcpy(csingle->actions, authtype_actions[grouptype],
                       sizeof csingle->actions);
        }
        rad_assert(modrefname != NULL);
@@ -2875,9 +2825,18 @@ static modcallable *do_compile_modgroup(modcallable *parent,
 
                        rad_assert(p->tail != NULL);
 
+                       /*
+                        *      We're in the process of compiling the
+                        *      section, so the parent's tail is the
+                        *      previous "if" statement.
+                        */
                        f = mod_callabletogroup(p->tail);
-                       rad_assert((f->mc.type == MOD_IF) ||
-                                  (f->mc.type == MOD_ELSIF));
+                       if ((f->mc.type != MOD_IF) &&
+                           (f->mc.type != MOD_ELSIF)) {
+                               cf_log_err_cs(g->cs, "Invalid location for 'elsif'.  There is no preceding 'if' statement");
+                               talloc_free(g);
+                               return NULL;
+                       }
 
                        /*
                         *      If we took the previous condition, we
@@ -2907,8 +2866,12 @@ static modcallable *do_compile_modgroup(modcallable *parent,
                        rad_assert(p->tail != NULL);
 
                        f = mod_callabletogroup(p->tail);
-                       rad_assert((f->mc.type == MOD_IF) ||
-                                  (f->mc.type == MOD_ELSIF));
+                       if ((f->mc.type != MOD_IF) &&
+                           (f->mc.type != MOD_ELSIF)) {
+                               cf_log_err_cs(g->cs, "Invalid location for 'else'.  There is no preceding 'if' statement");
+                               talloc_free(g);
+                               return NULL;
+                       }
 
                        /*
                         *      If we took the previous condition, we
@@ -3004,10 +2967,10 @@ set_codes:
         */
        for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
                if (!c->actions[i]) {
-                       if (!parent || (component != RLM_COMPONENT_AUTH)) {
+                       if (!parent || (component != MOD_AUTHENTICATE)) {
                                c->actions[i] = defaultactions[component][parentgrouptype][i];
                        } else { /* inside Auth-Type has different rules */
-                               c->actions[i] = defaultactions[RLM_COMPONENT_AUTZ][parentgrouptype][i];
+                               c->actions[i] = authtype_actions[parentgrouptype][i];
                        }
                }
        }
@@ -3043,7 +3006,7 @@ modcallable *compile_modgroup(modcallable *parent,
                                               GROUPTYPE_SIMPLE,
                                               GROUPTYPE_SIMPLE, MOD_GROUP);
 
-       if (debug_flag > 3) {
+       if (rad_debug_lvl > 3) {
                modcall_debug(ret, 2);
        }
 
@@ -3064,14 +3027,14 @@ void add_to_modcallable(modcallable *parent, modcallable *this)
 
 
 #ifdef WITH_UNLANG
-static bool pass2_xlat_compile(CONF_ITEM const *ci, value_pair_tmpl_t **pvpt, bool convert,
+static bool pass2_xlat_compile(CONF_ITEM const *ci, vp_tmpl_t **pvpt, bool convert,
                               DICT_ATTR const *da)
 {
        ssize_t slen;
        char *fmt;
        char const *error;
        xlat_exp_t *head;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        vpt = *pvpt;
 
@@ -3098,7 +3061,7 @@ static bool pass2_xlat_compile(CONF_ITEM const *ci, value_pair_tmpl_t **pvpt, bo
         *      Convert %{Attribute-Name} to &Attribute-Name
         */
        if (convert) {
-               value_pair_tmpl_t *attr;
+               vp_tmpl_t *attr;
 
                attr = xlat_to_tmpl_attr(talloc_parent(vpt), head);
                if (attr) {
@@ -3123,13 +3086,13 @@ static bool pass2_xlat_compile(CONF_ITEM const *ci, value_pair_tmpl_t **pvpt, bo
                        if (cf_item_is_pair(ci)) {
                                CONF_PAIR *cp = cf_item_to_pair(ci);
 
-                               WARN("%s[%d] Please change %%{%s} to &%s",
+                               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_item_to_section(ci);
 
-                               WARN("%s[%d] Please change %%{%s} to &%s",
+                               WARN("%s[%d]: Please change \"%%{%s}\" to &%s",
                                       cf_section_filename(cs), cf_section_lineno(cs),
                                       attr->name, attr->name);
                        }
@@ -3150,7 +3113,7 @@ static bool pass2_xlat_compile(CONF_ITEM const *ci, value_pair_tmpl_t **pvpt, bo
 
 
 #ifdef HAVE_REGEX
-static bool pass2_regex_compile(CONF_ITEM const *ci, value_pair_tmpl_t *vpt)
+static bool pass2_regex_compile(CONF_ITEM const *ci, vp_tmpl_t *vpt)
 {
        ssize_t slen;
        regex_t *preg;
@@ -3197,7 +3160,7 @@ static bool pass2_regex_compile(CONF_ITEM const *ci, value_pair_tmpl_t *vpt)
 }
 #endif
 
-static bool pass2_fixup_undefined(CONF_ITEM const *ci, value_pair_tmpl_t *vpt)
+static bool pass2_fixup_undefined(CONF_ITEM const *ci, vp_tmpl_t *vpt)
 {
        DICT_ATTR const *da;
 
@@ -3214,10 +3177,27 @@ static bool pass2_fixup_undefined(CONF_ITEM const *ci, value_pair_tmpl_t *vpt)
        return true;
 }
 
-static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
+static bool pass2_callback(void *ctx, fr_cond_t *c)
 {
-       value_pair_map_t *map;
+       vp_map_t *map;
+       vp_tmpl_t *vpt;
+
+       /*
+        *      These don't get optimized.
+        */
+       if ((c->type == COND_TYPE_TRUE) ||
+           (c->type == COND_TYPE_FALSE)) {
+               return true;
+       }
+
+       /*
+        *      Call children.
+        */
+       if (c->type == COND_TYPE_CHILD) return pass2_callback(ctx, c->data.child);
 
+       /*
+        *      A few simple checks here.
+        */
        if (c->type == COND_TYPE_EXISTS) {
                if (c->data.vpt->type == TMPL_TYPE_XLAT) {
                        return pass2_xlat_compile(c->ci, &c->data.vpt, true, NULL);
@@ -3233,22 +3213,24 @@ static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
                        if (!pass2_fixup_undefined(c->ci, c->data.vpt)) return false;
                        c->pass2_fixup = PASS2_FIXUP_NONE;
                }
-               return true;
-       }
 
-       /*
-        *      Maps have a paircompare fixup applied to them.
-        *      Others get ignored.
-        */
-       if (c->pass2_fixup == PASS2_FIXUP_NONE) {
-               if (c->type == COND_TYPE_MAP) {
-                       map = c->data.map;
-                       goto check_paircmp;
+               /*
+                *      Convert virtual &Attr-Foo to "%{Attr-Foo}"
+                */
+               vpt = c->data.vpt;
+               if ((vpt->type == TMPL_TYPE_ATTR) && vpt->tmpl_da->flags.virtual) {
+                       vpt->tmpl_xlat = xlat_from_tmpl_attr(vpt, vpt);
+                       vpt->type = TMPL_TYPE_XLAT_STRUCT;
                }
 
                return true;
        }
 
+       /*
+        *      And tons of complicated checks.
+        */
+       rad_assert(c->type == COND_TYPE_MAP);
+
        map = c->data.map;      /* shorter */
 
        /*
@@ -3285,7 +3267,6 @@ static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
                c->pass2_fixup = PASS2_FIXUP_NONE;
        }
 
-check_paircmp:
        /*
         *      Just in case someone adds a new fixup later.
         */
@@ -3297,13 +3278,88 @@ check_paircmp:
         */
        if (map->lhs->type == TMPL_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 TMPL_TYPE_DATA.
+                *      Compile the LHS to an attribute reference only
+                *      if the RHS is a literal.
+                *
+                *      @todo v3.1: allow anything anywhere.
                 */
-               if (!pass2_xlat_compile(map->ci, &map->lhs, false, NULL)) {
-                       return false;
+               if (map->rhs->type != TMPL_TYPE_LITERAL) {
+                       if (!pass2_xlat_compile(map->ci, &map->lhs, false, NULL)) {
+                               return false;
+                       }
+               } else {
+                       if (!pass2_xlat_compile(map->ci, &map->lhs, true, NULL)) {
+                               return false;
+                       }
+
+                       /*
+                        *      Attribute compared to a literal gets
+                        *      the literal cast to the data type of
+                        *      the attribute.
+                        *
+                        *      The code in parser.c did this for
+                        *
+                        *              &Attr == data
+                        *
+                        *      But now we've just converted "%{Attr}"
+                        *      to &Attr, so we've got to do it again.
+                        */
+                       if ((map->lhs->type == TMPL_TYPE_ATTR) &&
+                           (map->rhs->type == TMPL_TYPE_LITERAL)) {
+                               /*
+                                *      RHS is hex, try to parse it as
+                                *      type-specific data.
+                                */
+                               if (map->lhs->auto_converted &&
+                                   (map->rhs->name[0] == '0') && (map->rhs->name[1] == 'x') &&
+                                   (map->rhs->len > 2) && ((map->rhs->len & 0x01) == 0)) {
+                                       vpt = map->rhs;
+                                       map->rhs = NULL;
+
+                                       if (!map_cast_from_hex(map, T_BARE_WORD, vpt->name)) {
+                                               map->rhs = vpt;
+                                               cf_log_err(map->ci, "%s", fr_strerror());
+                                               return -1;
+                                       }
+                                       talloc_free(vpt);
+
+                               } else if ((map->rhs->len > 0) ||
+                                          (map->op != T_OP_CMP_EQ) ||
+                                          (map->lhs->tmpl_da->type == PW_TYPE_STRING) ||
+                                          (map->lhs->tmpl_da->type == PW_TYPE_OCTETS)) {
+
+                                       if (tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) {
+                                               cf_log_err(map->ci, "Failed to parse data type %s from string: %s",
+                                                          fr_int2str(dict_attr_types, map->lhs->tmpl_da->type, "<UNKNOWN>"),
+                                                          map->rhs->name);
+                                               return false;
+                                       } /* else the cast was successful */
+
+                               } else {        /* RHS is empty, it's just a check for empty / non-empty string */
+                                       vpt = talloc_steal(c, map->lhs);
+                                       map->lhs = NULL;
+                                       talloc_free(c->data.map);
+
+                                       /*
+                                        *      "%{Foo}" == '' ---> !Foo
+                                        *      "%{Foo}" != '' ---> Foo
+                                        */
+                                       c->type = COND_TYPE_EXISTS;
+                                       c->data.vpt = vpt;
+                                       c->negate = !c->negate;
+
+                                       WARN("%s[%d]: Please change (\"%%{%s}\" %s '') to %c&%s",
+                                            cf_section_filename(cf_item_to_section(c->ci)),
+                                            cf_section_lineno(cf_item_to_section(c->ci)),
+                                            vpt->name, c->negate ? "==" : "!=",
+                                            c->negate ? '!' : ' ', vpt->name);
+
+                                       /*
+                                        *      No more RHS, so we can't do more optimizations
+                                        */
+                                       return true;
+                               }
+                       }
                }
        }
 
@@ -3341,11 +3397,10 @@ check_paircmp:
            (strncmp(map->lhs->name, "Foreach-Variable-", 17) == 0)) {
                char *fmt;
                ssize_t slen;
-               value_pair_tmpl_t *vpt;
 
                fmt = talloc_asprintf(map->lhs, "%%{%s}", map->lhs->name);
                slen = tmpl_afrom_str(map, &vpt, fmt, talloc_array_length(fmt) - 1,
-                                     T_DOUBLE_QUOTED_STRING, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                                     T_DOUBLE_QUOTED_STRING, REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
                if (slen < 0) {
                        char *spaces, *text;
 
@@ -3375,6 +3430,32 @@ check_paircmp:
 #endif
 
        /*
+        *      Convert &Packet-Type to "%{Packet-Type}", because
+        *      these attributes don't really exist.  The code to
+        *      find an attribute reference doesn't work, but the
+        *      xlat code does.
+        */
+       vpt = c->data.map->lhs;
+       if ((vpt->type == TMPL_TYPE_ATTR) && vpt->tmpl_da->flags.virtual) {
+               if (!c->cast) c->cast = vpt->tmpl_da;
+               vpt->tmpl_xlat = xlat_from_tmpl_attr(vpt, vpt);
+               vpt->type = TMPL_TYPE_XLAT_STRUCT;
+       }
+
+       /*
+        *      Convert RHS to expansions, too.
+        */
+       vpt = c->data.map->rhs;
+       if ((vpt->type == TMPL_TYPE_ATTR) && vpt->tmpl_da->flags.virtual) {
+               vpt->tmpl_xlat = xlat_from_tmpl_attr(vpt, vpt);
+               vpt->type = TMPL_TYPE_XLAT_STRUCT;
+       }
+
+       /*
+        *      @todo v3.1: do the same thing for the RHS...
+        */
+
+       /*
         *      Only attributes can have a paircompare registered, and
         *      they can only be with the current REQUEST, and only
         *      with the request pairs.
@@ -3413,7 +3494,7 @@ check_paircmp:
 
        /*
         *      Mark it as requiring a paircompare() call, instead of
-        *      paircmp().
+        *      fr_pair_cmp().
         */
        c->pass2_fixup = PASS2_PAIRCOMPARE;
 
@@ -3426,7 +3507,7 @@ check_paircmp:
  */
 static bool modcall_pass2_update(modgroup *g)
 {
-       value_pair_map_t *map;
+       vp_map_t *map;
 
        for (map = g->map; map != NULL; map = map->next) {
                if (map->rhs->type == TMPL_TYPE_XLAT) {
@@ -3550,7 +3631,7 @@ bool modcall_pass2(modcallable *mc)
 
                                slen = tmpl_afrom_str(g->cs, &g->vpt, c->name, strlen(c->name),
                                                      cf_section_name2_type(g->cs),
-                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
                                if (slen < 0) {
                                        char *spaces, *text;
 
@@ -3583,6 +3664,14 @@ bool modcall_pass2(modcallable *mc)
                        }
 
                        /*
+                        *      Convert virtual &Attr-Foo to "%{Attr-Foo}"
+                        */
+                       if ((g->vpt->type == TMPL_TYPE_ATTR) && g->vpt->tmpl_da->flags.virtual) {
+                               g->vpt->tmpl_xlat = xlat_from_tmpl_attr(g->vpt, g->vpt);
+                               g->vpt->type = TMPL_TYPE_XLAT_STRUCT;
+                       }
+
+                       /*
                         *      We may have: switch Foo-Bar {
                         *
                         *      where Foo-Bar is an attribute defined
@@ -3592,10 +3681,10 @@ bool modcall_pass2(modcallable *mc)
                         *      switch to using that.
                         */
                        if (g->vpt->type == TMPL_TYPE_LITERAL) {
-                               value_pair_tmpl_t *vpt;
+                               vp_tmpl_t *vpt;
 
                                slen = tmpl_afrom_str(g->cs, &vpt, c->name, strlen(c->name), cf_section_name2_type(g->cs),
-                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
                                if (slen < 0) goto parse_error;
                                if (vpt->type == TMPL_TYPE_ATTR) {
                                        talloc_free(g->vpt);
@@ -3614,9 +3703,9 @@ bool modcall_pass2(modcallable *mc)
                        if ((g->vpt->type == TMPL_TYPE_ATTR) &&
                            (c->name[0] != '&')) {
                                WARN("%s[%d]: Please change %s to &%s",
-                                      cf_section_filename(g->cs),
-                                      cf_section_lineno(g->cs),
-                                      c->name, c->name);
+                                    cf_section_filename(g->cs),
+                                    cf_section_lineno(g->cs),
+                                    c->name, c->name);
                        }
 
                do_children:
@@ -3649,7 +3738,7 @@ bool modcall_pass2(modcallable *mc)
                            (cf_section_name2_type(g->cs) == T_BARE_WORD)) {
                                slen = tmpl_afrom_str(g->cs, &g->vpt, c->name, strlen(c->name),
                                                      cf_section_name2_type(g->cs),
-                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
                                if (slen < 0) goto parse_error;
                        }
 
@@ -3677,7 +3766,7 @@ bool modcall_pass2(modcallable *mc)
                                if (f->vpt->type == TMPL_TYPE_ATTR) {
                                        rad_assert(f->vpt->tmpl_da != NULL);
 
-                                       if (!tmpl_cast_in_place(g->vpt, f->vpt->tmpl_da->type, f->vpt->tmpl_da)) {
+                                       if (tmpl_cast_in_place(g->vpt, f->vpt->tmpl_da->type, f->vpt->tmpl_da) < 0) {
                                                cf_log_err_cs(g->cs, "Invalid argument for case statement: %s",
                                                              fr_strerror());
                                                return false;
@@ -3714,6 +3803,14 @@ bool modcall_pass2(modcallable *mc)
                                }
                        }
 
+                       /*
+                        *      Virtual attribute fixes for "case" statements, too.
+                        */
+                       if ((g->vpt->type == TMPL_TYPE_ATTR) && g->vpt->tmpl_da->flags.virtual) {
+                               g->vpt->tmpl_xlat = xlat_from_tmpl_attr(g->vpt, g->vpt);
+                               g->vpt->type = TMPL_TYPE_XLAT_STRUCT;
+                       }
+
                        if (!modcall_pass2(g->children)) return false;
                        g->done_pass2 = true;
                        break;
@@ -3745,7 +3842,7 @@ bool modcall_pass2(modcallable *mc)
                         *      Check for that now.
                         */
                        slen = tmpl_afrom_str(g->cs, &g->vpt, c->name, strlen(c->name), cf_section_name2_type(g->cs),
-                                             REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                                             REQUEST_CURRENT, PAIR_LIST_REQUEST, true);
                        if (slen < 0) goto parse_error;
 
                check_children:
@@ -3805,7 +3902,7 @@ void modcall_debug(modcallable *mc, int depth)
 {
        modcallable *this;
        modgroup *g;
-       value_pair_map_t *map;
+       vp_map_t *map;
        char buffer[1024];
 
        for (this = mc; this != NULL; this = this->next) {
index 10b939d..6053e0e 100644 (file)
@@ -42,8 +42,8 @@ typedef struct virtual_server_t {
        int             can_free;
        CONF_SECTION    *cs;
        rbtree_t        *components;
-       modcallable     *mc[RLM_COMPONENT_COUNT];
-       CONF_SECTION    *subcs[RLM_COMPONENT_COUNT];
+       modcallable     *mc[MOD_COUNT];
+       CONF_SECTION    *subcs[MOD_COUNT];
        struct virtual_server_t *next;
 } virtual_server_t;
 
@@ -67,7 +67,7 @@ struct fr_module_hup_t {
 /*
  *     Ordered by component
  */
-const section_type_value_t section_type_value[RLM_COMPONENT_COUNT] = {
+const section_type_value_t section_type_value[MOD_COUNT] = {
        { "authenticate", "Auth-Type",       PW_AUTH_TYPE },
        { "authorize",    "Autz-Type",       PW_AUTZ_TYPE },
        { "preacct",      "Pre-Acct-Type",   PW_PRE_ACCT_TYPE },
@@ -109,7 +109,15 @@ const section_type_value_t section_type_value[RLM_COMPONENT_COUNT] = {
  */
 static int check_module_magic(CONF_SECTION *cs, module_t const *module)
 {
+#ifdef HAVE_DLADDR
+       Dl_info dl_info;
+       dladdr(module, &dl_info);
+#endif
+
        if (MAGIC_PREFIX(module->magic) != MAGIC_PREFIX(RADIUSD_MAGIC_NUMBER)) {
+#ifdef HAVE_DLADDR
+               cf_log_err_cs(cs, "Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
+#endif
                cf_log_err_cs(cs, "Application and rlm_%s magic number (prefix) mismatch."
                              "  application: %x module: %x", module->name,
                              MAGIC_PREFIX(RADIUSD_MAGIC_NUMBER),
@@ -118,6 +126,9 @@ static int check_module_magic(CONF_SECTION *cs, module_t const *module)
        }
 
        if (MAGIC_VERSION(module->magic) != MAGIC_VERSION(RADIUSD_MAGIC_NUMBER)) {
+#ifdef HAVE_DLADDR
+               cf_log_err_cs(cs, "Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
+#endif
                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),
@@ -126,6 +137,9 @@ static int check_module_magic(CONF_SECTION *cs, module_t const *module)
        }
 
        if (MAGIC_COMMIT(module->magic) != MAGIC_COMMIT(RADIUSD_MAGIC_NUMBER)) {
+#ifdef HAVE_DLADDR
+               cf_log_err_cs(cs, "Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
+#endif
                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),
@@ -138,10 +152,10 @@ static int check_module_magic(CONF_SECTION *cs, module_t const *module)
 
 lt_dlhandle lt_dlopenext(char const *name)
 {
-       int flags = RTLD_NOW;
-       void *handle;
-       char buffer[2048];
-       char *env;
+       int     flags = RTLD_NOW;
+       void    *handle;
+       char    buffer[2048];
+       char    *env;
 
 #ifdef RTLD_GLOBAL
        if (strcmp(name, "rlm_perl") == 0) {
@@ -159,31 +173,42 @@ lt_dlhandle lt_dlopenext(char const *name)
        /*
         *      Prefer loading our libraries by absolute path.
         */
-       snprintf(buffer, sizeof(buffer), "%s/%s%s", radlib_dir, name, LT_SHREXT);
+       if (radlib_dir) {
+               char *error;
 
-       DEBUG4("Loading library using absolute path \"%s\"", name);
+               snprintf(buffer, sizeof(buffer), "%s/%s%s", radlib_dir, name, LT_SHREXT);
 
-       handle = dlopen(buffer, flags);
-       if (handle) return handle;
+               DEBUG4("Loading library using absolute path \"%s\"", buffer);
 
-       /*
-        *      Because dlopen produces really shitty and inaccurate error messages
-        */
-       if (access(name, R_OK) < 0) switch (errno) {
-       case EACCES:
-               WARN("Library file found, but we don't have permission to read it");
-               break;
+               handle = dlopen(buffer, flags);
+               if (handle) return handle;
+               error = dlerror();
 
-       case ENOENT:
-               DEBUG4("Library file not found");
-               break;
+               fr_strerror_printf("%s", error);
+               DEBUG4("Failed with error: %s", error);
 
-       default:
-               DEBUG4("Issue accessing library file: %s", fr_syserror(errno));
-               break;
+               /*
+                *      Because dlopen (on OSX at least) produces really
+                *      shitty and inaccurate error messages
+                */
+               if (access(buffer, R_OK) < 0) {
+                       switch (errno) {
+                       case EACCES:
+                               WARN("Library file found, but we don't have permission to read it");
+                               break;
+
+                       case ENOENT:
+                               DEBUG4("Library file not found");
+                               break;
+
+                       default:
+                               DEBUG4("Issue accessing library file: %s", fr_syserror(errno));
+                               break;
+                       }
+               }
        }
 
-       DEBUG4("Falling back to linker search path(s)");
+       DEBUG4("Loading library using linker search path(s)");
        if (DEBUG_ENABLED4) {
 #ifdef __APPLE__
                env = getenv("LD_LIBRARY_PATH");
@@ -217,7 +242,19 @@ lt_dlhandle lt_dlopenext(char const *name)
         */
        strlcat(buffer, LT_SHREXT, sizeof(buffer));
 
-       return dlopen(buffer, flags);
+       handle = dlopen(buffer, flags);
+       if (!handle) {
+               char *error = dlerror();
+
+               DEBUG4("Failed with error: %s", error);
+               /*
+                *      Don't overwrite the previous message
+                *      It's likely to contain a better error.
+                */
+               if (!radlib_dir) fr_strerror_printf("%s", dlerror());
+               return NULL;
+       }
+       return handle;
 }
 
 void *lt_dlsym(lt_dlhandle handle, char const *symbol)
@@ -434,9 +471,9 @@ int modules_free(void)
 
 
 /*
- *     Find a module on disk or in memory, and link to it.
+ *     dlopen() a module.
  */
-static module_entry_t *linkto_module(char const *module_name, CONF_SECTION *cs)
+static module_entry_t *module_dlopen(CONF_SECTION *cs, char const *module_name)
 {
        module_entry_t myentry;
        module_entry_t *node;
@@ -462,7 +499,7 @@ static module_entry_t *linkto_module(char const *module_name, CONF_SECTION *cs)
         */
        handle = lt_dlopenext(module_name);
        if (!handle) {
-               cf_log_err_cs(cs, "Failed to link to module '%s': %s", module_name, dlerror());
+               cf_log_err_cs(cs, "Failed to link to module '%s': %s", module_name, fr_strerror());
                return NULL;
        }
 
@@ -521,15 +558,11 @@ static int module_conf_parse(module_instance_t *node, void **handle)
         *      Also parse the configuration data, if required.
         */
        if (node->entry->module->inst_size) {
-               /* FIXME: make this rlm_config_t ?? */
                *handle = talloc_zero_array(node, uint8_t, node->entry->module->inst_size);
                rad_assert(handle);
 
-               /*
-                *      So we can see where this configuration is from
-                *      FIXME: set it to rlm_NAME_t, or some such thing
-                */
-               talloc_set_name(*handle, "rlm_config_t");
+               talloc_set_name(*handle, "rlm_%s_t",
+                               node->entry->module->name ? node->entry->module->name : "config");
 
                if (node->entry->module->config &&
                    (cf_section_parse(node->cs, *handle, node->entry->module->config) < 0)) {
@@ -550,65 +583,48 @@ static int module_conf_parse(module_instance_t *node, void **handle)
        return 0;
 }
 
-/*
- *     Find a module instance.
+/** Bootstrap a module.
+ *
+ *  Load the module shared library, allocate instance memory for it,
+ *  parse the module configuration, and call the modules "bootstrap" method.
  */
-module_instance_t *find_module_instance(CONF_SECTION *modules,
-                                       char const *askedname, bool do_link)
+static module_instance_t *module_bootstrap(CONF_SECTION *cs)
 {
-       bool check_config_safe = false;
-       CONF_SECTION *cs;
-       char const *name1, *instname;
+       char const *name1, *name2;
        module_instance_t *node, myNode;
        char module_name[256];
 
-       if (!modules) return NULL;
-
        /*
-        *      Look for the real name.  Ignore the first character,
-        *      which tells the server "it's OK for this module to not
-        *      exist."
+        *      Figure out which module we want to load.
         */
-       instname = askedname;
-       if (instname[0] == '-') {
-               instname++;
-       }
+       name1 = cf_section_name1(cs);
+       name2 = cf_section_name2(cs);
+       if (!name2) name2 = name1;
 
-       /*
-        *      Module instances are declared in the modules{} block
-        *      and referenced later by their name, which is the
-        *      name2 from the config section, or name1 if there was
-        *      no name2.
-        */
-       cs = cf_section_sub_find_name2(modules, NULL, instname);
-       if (!cs) {
-               ERROR("Cannot find a configuration entry for module \"%s\"", instname);
-               return NULL;
-       }
+       strlcpy(myNode.name, name2, sizeof(myNode.name));
 
        /*
-        *      If there's already a module instance, return it.
+        *      See if the module already exists.
         */
-       strlcpy(myNode.name, instname, sizeof(myNode.name));
-
        node = rbtree_finddata(instance_tree, &myNode);
        if (node) {
-               return node;
-       }
-
-       if (!do_link) {
+               ERROR("Duplicate module \"%s %s\", in file %s:%d and file %s:%d",
+                     name1, name2,
+                     cf_section_filename(cs),
+                     cf_section_lineno(cs),
+                     cf_section_filename(node->cs),
+                     cf_section_lineno(node->cs));
                return NULL;
        }
 
-       name1 = cf_section_name1(cs);
-
        /*
-        *      Found the configuration entry, hang the node struct off of the
-        *      configuration section. If the CS is free'd the instance will
-        *      be too.
+        *      Hang the node struct off of the configuration
+        *      section. If the CS is free'd the instance will be
+        *      free'd, too.
         */
        node = talloc_zero(cs, module_instance_t);
        node->cs = cs;
+       strlcpy(node->name, name2, sizeof(node->name));
 
        /*
         *      Names in the "modules" section aren't prefixed
@@ -617,60 +633,116 @@ module_instance_t *find_module_instance(CONF_SECTION *modules,
        snprintf(module_name, sizeof(module_name), "rlm_%s", name1);
 
        /*
-        *      Pull in the module object
+        *      Load the module shared library.
         */
-       node->entry = linkto_module(module_name, cs);
+       node->entry = module_dlopen(cs, module_name);
        if (!node->entry) {
                talloc_free(node);
-               /* linkto_module logs any errors */
                return NULL;
        }
 
-       if (check_config && (node->entry->module->instantiate) &&
-           (node->entry->module->type & RLM_TYPE_CHECK_CONFIG_UNSAFE) != 0) {
-               char const *value = NULL;
-               CONF_PAIR *cp;
-
-               cp = cf_pair_find(cs, "force_check_config");
-               if (cp) {
-                       value = cf_pair_value(cp);
-               }
+       cf_log_module(cs, "Loading module \"%s\" from file %s", node->name,
+                     cf_section_filename(cs));
 
-               if (value && (strcmp(value, "yes") == 0)) goto print_inst;
+       /*
+        *      Parse the modules configuration.
+        */
+       if (module_conf_parse(node, &node->insthandle) < 0) {
+               talloc_free(node);
+               return NULL;
+       }
 
-               cf_log_module(cs, "Skipping instantiation of %s", instname);
-       } else {
-       print_inst:
-               check_config_safe = true;
-               cf_log_module(cs, "Instantiating module \"%s\" from file %s", instname,
-                             cf_section_filename(cs));
+       /*
+        *      Bootstrap the module.
+        */
+       if (node->entry->module->bootstrap &&
+           ((node->entry->module->bootstrap)(cs, node->insthandle) < 0)) {
+               cf_log_err_cs(cs, "Instantiation failed for module \"%s\"", node->name);
+               talloc_free(node);
+               return NULL;
        }
 
-       strlcpy(node->name, instname, sizeof(node->name));
+       /*
+        *      Remember the module for later.
+        */
+       rbtree_insert(instance_tree, node);
+
+       return node;
+}
+
+
+/** Find an existing module instance.
+ *
+ */
+module_instance_t *module_find(CONF_SECTION *modules, char const *askedname)
+{
+       char const *instname;
+       module_instance_t myNode;
+
+       if (!modules) return NULL;
 
        /*
-        *      Parse the module configuration, and setup destructors so the
-        *      module's detach method is called when it's instance data is
-        *      about to be freed.
+        *      Look for the real name.  Ignore the first character,
+        *      which tells the server "it's OK for this module to not
+        *      exist."
         */
-       if (module_conf_parse(node, &node->insthandle) < 0) {
-               talloc_free(node);
+       instname = askedname;
+       if (instname[0] == '-') instname++;
 
+       strlcpy(myNode.name, instname, sizeof(myNode.name));
+
+       return rbtree_finddata(instance_tree, &myNode);
+}
+
+
+/** Load a module, and instantiate it.
+ *
+ */
+module_instance_t *module_instantiate(CONF_SECTION *modules, char const *askedname)
+{
+       module_instance_t *node;
+
+       /*
+        *      Find the module.  If it's not there, do nothing.
+        */
+       node = module_find(modules, askedname);
+       if (!node) {
+               ERROR("Cannot find module \"%s\"", askedname);
                return NULL;
        }
 
        /*
-        *      Call the module's instantiation routine.
+        *      The module is already instantiated.  Return it.
         */
-       if ((node->entry->module->instantiate) &&
-           (!check_config || check_config_safe) &&
-           ((node->entry->module->instantiate)(cs, node->insthandle) < 0)) {
-               cf_log_err_cs(cs, "Instantiation failed for module \"%s\"", node->name);
-               talloc_free(node);
+       if (node->instantiated) return node;
 
+       /*
+        *      Now that ALL modules are instantiated, and ALL xlats
+        *      are defined, go compile the config items marked as XLAT.
+        */
+       if (node->entry->module->config &&
+           (cf_section_parse_pass2(node->cs, node->insthandle,
+                                   node->entry->module->config) < 0)) {
                return NULL;
        }
 
+       /*
+        *      Call the instantiate method, if any.
+        */
+       if (node->entry->module->instantiate) {
+               cf_log_module(node->cs, "Instantiating module \"%s\" from file %s", node->name,
+                             cf_section_filename(node->cs));
+
+               /*
+                *      Call the module's instantiation routine.
+                */
+               if ((node->entry->module->instantiate)(node->cs, node->insthandle) < 0) {
+                       cf_log_err_cs(node->cs, "Instantiation failed for module \"%s\"", node->name);
+
+                       return NULL;
+               }
+       }
+
 #ifdef HAVE_PTHREAD_H
        /*
         *      If we're threaded, check if the module is thread-safe.
@@ -684,19 +756,64 @@ module_instance_t *find_module_instance(CONF_SECTION *modules,
                 *      Initialize the mutex.
                 */
                pthread_mutex_init(node->mutex, NULL);
-       } else {
-               /*
-                *      The module is thread-safe.  Don't give it a mutex.
-                */
-               node->mutex = NULL;
        }
-
 #endif
-       rbtree_insert(instance_tree, node);
+
+       node->instantiated = true;
+       node->last_hup = time(NULL); /* don't let us load it, then immediately hup it */
 
        return node;
 }
 
+
+module_instance_t *module_instantiate_method(CONF_SECTION *modules, char const *name, rlm_components_t *method)
+{
+       char *p;
+       rlm_components_t i;
+       module_instance_t *mi;
+
+       /*
+        *      If the module exists, ensure it's instantiated.
+        *
+        *      Doing it this way avoids complaints from
+        *      module_instantiate()
+        */
+       mi = module_find(modules, name);
+       if (mi) return module_instantiate(modules, name);
+
+       /*
+        *      Find out which method is being used.
+        */
+       p = strrchr(name, '.');
+       if (!p) return NULL;
+
+       p++;
+
+       /*
+        *      Find the component.
+        */
+       for (i = MOD_AUTHENTICATE; i < MOD_COUNT; i++) {
+               if (strcmp(p, section_type_value[i].section) == 0) {
+                       char buffer[256];
+
+                       strlcpy(buffer, name, sizeof(buffer));
+                       buffer[p - name - 1] = '\0';
+
+                       mi = module_find(modules, buffer);
+                       if (mi) {
+                               if (method) *method = i;
+                               return module_instantiate(modules, buffer);
+                       }
+               }
+       }
+
+       /*
+        *      Not found.
+        */
+       return NULL;
+}
+
+
 /** Resolve polymorphic item's from a module's CONF_SECTION to a subsection in another module
  *
  * This allows certain module sections to reference module sections in other instances
@@ -764,7 +881,7 @@ int find_module_sibling_section(CONF_SECTION **out, CONF_SECTION *module, char c
         *      instantiation order issues.
         */
        inst_name = cf_pair_value(cp);
-       inst = find_module_instance(cf_item_parent(cf_section_to_item(module)), inst_name, true);
+       inst = module_instantiate(cf_item_parent(cf_section_to_item(module)), inst_name);
 
        /*
         *      Remove the config data we added for loop
@@ -858,8 +975,14 @@ rlm_rcode_t indexed_modcall(rlm_components_t comp, int idx, REQUEST *request)
 
        if (idx == 0) {
                list = server->mc[comp];
-               if (!list) RDEBUG3("Empty %s section.  Using default return values.", section_type_value[comp].section);
-
+               if (!list) {
+                       if (server->name) {
+                               RDEBUG3("Empty %s section in virtual server \"%s\".  Using default return values.",
+                                       section_type_value[comp].section, server->name);
+                       } else {
+                               RDEBUG3("Empty %s section.  Using default return values.", section_type_value[comp].section);
+                       }
+               }
        } else {
                indexed_modcallable *this;
 
@@ -867,8 +990,7 @@ rlm_rcode_t indexed_modcall(rlm_components_t comp, int idx, REQUEST *request)
                if (this) {
                        list = this->modulelist;
                } else {
-                       RDEBUG3("%s sub-section not found.  Ignoring.",
-                               section_type_value[comp].typename);
+                       RDEBUG2("%s sub-section not found.  Ignoring.", section_type_value[comp].typename);
                }
        }
 
@@ -1020,7 +1142,7 @@ static int load_component_section(CONF_SECTION *cs,
                 *      i.e. They're not allowed in a "group" or "redundant"
                 *      subsection.
                 */
-               if (comp == RLM_COMPONENT_AUTH) {
+               if (comp == MOD_AUTHENTICATE) {
                        DICT_VALUE *dval;
                        char const *modrefname = NULL;
                        if (cp) {
@@ -1096,7 +1218,7 @@ static int load_component_section(CONF_SECTION *cs,
                        return -1;
                }
 
-               if (debug_flag > 2) modcall_debug(this, 2);
+               if (rad_debug_lvl > 2) modcall_debug(this, 2);
 
                add_to_modcallable(subcomp->modulelist, this);
        }
@@ -1112,6 +1234,7 @@ static int load_byserver(CONF_SECTION *cs)
        rbtree_t *components;
        virtual_server_t *server = NULL;
        indexed_modcallable *c;
+       bool is_bare;
 
        if (name) {
                cf_log_info(cs, "server %s { # from file %s",
@@ -1121,6 +1244,8 @@ static int load_byserver(CONF_SECTION *cs)
                            cf_section_filename(cs));
        }
 
+       is_bare = (cf_item_parent(cf_section_to_item(cs)) == NULL);
+
        server = talloc_zero(cs, virtual_server_t);
        server->name = name;
        server->created = time(NULL);
@@ -1130,7 +1255,7 @@ static int load_byserver(CONF_SECTION *cs)
                ERROR("Failed to initialize components");
 
        error:
-               if (debug_flag == 0) {
+               if (rad_debug_lvl == 0) {
                        ERROR("Failed to load virtual server %s",
                              (name != NULL) ? name : "<default>");
                }
@@ -1143,14 +1268,14 @@ static int load_byserver(CONF_SECTION *cs)
         *      configuration section, and loading it.
         */
        found = 0;
-       for (comp = 0; comp < RLM_COMPONENT_COUNT; ++comp) {
+       for (comp = 0; comp < MOD_COUNT; ++comp) {
                CONF_SECTION *subcs;
 
                subcs = cf_section_sub_find(cs,
                                            section_type_value[comp].section);
                if (!subcs) continue;
 
-               if (!name) {
+               if (is_bare) {
                        cf_log_err_cs(subcs, "The %s section should be inside of a 'server { ... }' block!",
                                      section_type_value[comp].section);
                }
@@ -1165,20 +1290,20 @@ static int load_byserver(CONF_SECTION *cs)
 #ifdef WITH_PROXY
                    !main_config.proxy_requests &&
 #endif
-                   ((comp == RLM_COMPONENT_PRE_PROXY) ||
-                    (comp == RLM_COMPONENT_POST_PROXY))) {
+                   ((comp == MOD_PRE_PROXY) ||
+                    (comp == MOD_POST_PROXY))) {
                        continue;
                }
 
 #ifndef WITH_ACCOUNTING
-               if (comp == RLM_COMPONENT_ACCT) continue;
+               if (comp == MOD_ACCOUNTING) continue;
 #endif
 
 #ifndef WITH_SESSION_MGMT
-               if (comp == RLM_COMPONENT_SESS) continue;
+               if (comp == MOD_SESSION) continue;
 #endif
 
-               if (debug_flag <= 3) {
+               if (rad_debug_lvl <= 3) {
                        cf_log_module(cs, "Loading %s {...}",
                                      section_type_value[comp].section);
                } else {
@@ -1189,7 +1314,7 @@ static int load_byserver(CONF_SECTION *cs)
                        goto error;
                }
 
-               if (debug_flag > 3) {
+               if (rad_debug_lvl > 3) {
                        DEBUG(" } # %s", section_type_value[comp].section);
                }
 
@@ -1224,12 +1349,12 @@ static int load_byserver(CONF_SECTION *cs)
                if (subcs) {
                        cf_log_module(cs, "Loading vmps {...}");
                        if (load_component_section(subcs, components,
-                                                  RLM_COMPONENT_POST_AUTH) < 0) {
+                                                  MOD_POST_AUTH) < 0) {
                                goto error;
                        }
                        c = lookup_by_index(components,
-                                           RLM_COMPONENT_POST_AUTH, 0);
-                       if (c) server->mc[RLM_COMPONENT_POST_AUTH] = c->modulelist;
+                                           MOD_POST_AUTH, 0);
+                       if (c) server->mc[MOD_POST_AUTH] = c->modulelist;
                        break;
                }
 #endif
@@ -1257,12 +1382,12 @@ static int load_byserver(CONF_SECTION *cs)
                        if (!load_subcomponent_section(subcs,
                                                       components,
                                                       da,
-                                                      RLM_COMPONENT_POST_AUTH)) {
+                                                      MOD_POST_AUTH)) {
                                goto error; /* FIXME: memleak? */
                        }
                        c = lookup_by_index(components,
-                                           RLM_COMPONENT_POST_AUTH, 0);
-                       if (c) server->mc[RLM_COMPONENT_POST_AUTH] = c->modulelist;
+                                           MOD_POST_AUTH, 0);
+                       if (c) server->mc[MOD_POST_AUTH] = c->modulelist;
 
                        subcs = cf_subsection_find_next(cs, subcs, "dhcp");
                }
@@ -1275,7 +1400,7 @@ static int load_byserver(CONF_SECTION *cs)
                cf_log_info(cs, "} # server");
        }
 
-       if (debug_flag == 0) {
+       if (rad_debug_lvl == 0) {
                INFO("Loaded virtual server %s",
                       (name != NULL) ? name : "<default>");
        }
@@ -1316,19 +1441,6 @@ static int pass2_cb(UNUSED void *ctx, void *data)
        return 0;
 }
 
-static int pass2_instance_cb(UNUSED void *ctx, void *data)
-{
-       module_instance_t *node = data;
-
-       if (!node->entry->module->config || !node->cs) return 0;
-
-       if (cf_section_parse_pass2(node->cs, node->insthandle,
-                                  node->entry->module->config) < 0) {
-               return -1;
-       }
-
-       return 0;
-}
 
 /*
  *     Load all of the virtual servers.
@@ -1397,14 +1509,6 @@ int virtual_servers_load(CONF_SECTION *config)
        }
 
        /*
-        *      Check all of the module config items which are xlat expanded.
-        */
-       if (rbtree_walk(instance_tree, RBTREE_IN_ORDER,
-                       pass2_instance_cb, NULL) != 0) {
-               return -1;
-       }
-
-       /*
         *      Try to compile the "authorize", etc. sections which
         *      aren't in a virtual server.
         */
@@ -1412,7 +1516,7 @@ int virtual_servers_load(CONF_SECTION *config)
        if (server) {
                int i;
 
-               for (i = RLM_COMPONENT_AUTH; i < RLM_COMPONENT_COUNT; i++) {
+               for (i = MOD_AUTHENTICATE; i < MOD_COUNT; i++) {
                        if (!modcall_pass2(server->mc[i])) return -1;
                }
 
@@ -1438,7 +1542,7 @@ int virtual_servers_load(CONF_SECTION *config)
                server = virtual_server_find(name2);
                if (!server) continue;
 
-               for (i = RLM_COMPONENT_AUTH; i < RLM_COMPONENT_COUNT; i++) {
+               for (i = MOD_AUTHENTICATE; i < MOD_COUNT; i++) {
                        if (!modcall_pass2(server->mc[i])) return -1;
                }
 
@@ -1463,11 +1567,18 @@ int module_hup_module(CONF_SECTION *cs, module_instance_t *node, time_t when)
        fr_module_hup_t *mh;
 
        if (!node ||
+           node->entry->module->bootstrap ||
            !node->entry->module->instantiate ||
            ((node->entry->module->type & RLM_TYPE_HUP_SAFE) == 0)) {
                return 1;
        }
 
+       /*
+        *      Silently ignore multiple HUPs within a short time period.
+        */
+       if ((node->last_hup + 2) >= when) return 1;
+       node->last_hup = when;
+
        cf_log_module(cs, "Trying to reload module \"%s\"", node->name);
 
        /*
@@ -1503,6 +1614,9 @@ int module_hup_module(CONF_SECTION *cs, module_instance_t *node, time_t when)
        mh->next = node->mh;
        node->mh = mh;
 
+       /*
+        *      Replace the instance handle while the module is running.
+        */
        node->insthandle = insthandle;
 
        /*
@@ -1563,7 +1677,14 @@ static int define_type(CONF_SECTION *cs, DICT_ATTR const *da, char const *name)
         *      create it again.
         */
        dval = dict_valbyname(da->attr, da->vendor, name);
-       if (dval) return 1;
+       if (dval) {
+               if (dval->value == 0) {
+                       ERROR("The dictionaries must not define VALUE %s %s 0",
+                             da->name, name);
+                       return 0;
+               }
+               return 1;
+       }
 
        /*
         *      Create a new unique value with a
@@ -1573,7 +1694,7 @@ static int define_type(CONF_SECTION *cs, DICT_ATTR const *da, char const *name)
         *      is that it's unique.
         */
        do {
-               value = fr_rand() & 0x00ffffff;
+               value = (fr_rand() & 0x00ffffff) + 1;
        } while (dict_valbyattr(da->attr, da->vendor, value));
 
        cf_log_module(cs, "Creating %s = %s", da->name, name);
@@ -1595,7 +1716,7 @@ static bool server_define_types(CONF_SECTION *cs)
        /*
         *      Loop over all of the components
         */
-       for (comp = 0; comp < RLM_COMPONENT_COUNT; ++comp) {
+       for (comp = 0; comp < MOD_COUNT; ++comp) {
                CONF_SECTION *subcs;
                CONF_ITEM *modref;
                DICT_ATTR const *da;
@@ -1787,40 +1908,30 @@ int modules_init(CONF_SECTION *config)
         *      This is O(N^2) in the number of modules, but most
         *      systems should have less than 100 modules.
         */
-       for (ci=cf_item_find_next(modules, NULL);
+       for (ci = cf_item_find_next(modules, NULL);
             ci != NULL;
-            ci=next) {
-               char const *name1, *name2;
-               CONF_SECTION *subcs, *duplicate;
+            ci = next) {
+               char const *name1;
+               CONF_SECTION *subcs;
+               module_instance_t *node;
 
                next = cf_item_find_next(modules, ci);
 
                if (!cf_item_is_section(ci)) continue;
 
+               subcs = cf_item_to_section(ci);
+
+               node = module_bootstrap(subcs);
+               if (!node) return -1;
+
                if (!next || !cf_item_is_section(next)) continue;
 
-               subcs = cf_item_to_section(ci);
                name1 = cf_section_name1(subcs);
-               name2 = cf_section_name2(subcs);
 
                if (is_reserved_word(name1)) {
                        cf_log_err_cs(subcs, "Module cannot be named for an 'unlang' keyword");
                        return -1;
                }
-
-               duplicate = cf_section_find_name2(cf_item_to_section(next),
-                                                 name1, name2);
-               if (!duplicate) continue;
-
-               if (!name2) name2 = "";
-
-               ERROR("Duplicate module \"%s %s\", in file %s:%d and file %s:%d",
-                      name1, name2,
-                      cf_section_filename(subcs),
-                      cf_section_lineno(subcs),
-                      cf_section_filename(duplicate),
-                      cf_section_lineno(duplicate));
-               return -1;
        }
 
        /*
@@ -1843,7 +1954,6 @@ int modules_init(CONF_SECTION *config)
                for (ci=cf_item_find_next(cs, NULL);
                     ci != NULL;
                     ci=cf_item_find_next(cs, ci)) {
-
                        /*
                         *      Skip sections and "other" stuff.
                         *      Sections will be handled later, if
@@ -1853,7 +1963,7 @@ int modules_init(CONF_SECTION *config)
                                cp = cf_item_to_pair(ci);
                                name = cf_pair_attr(cp);
 
-                               module = find_module_instance(modules, name, true);
+                               module = module_instantiate(modules, name);
                                if (!module && (name[0] != '-')) {
                                        return -1;
                                }
@@ -1911,7 +2021,10 @@ int modules_init(CONF_SECTION *config)
                                                        return -1;
                                                }
 
-                                               module = find_module_instance(modules, cf_pair_attr(cp), true);
+                                               /*
+                                                *      Allow "foo.authorize" in subsections.
+                                                */
+                                               module = module_instantiate_method(modules, cf_pair_attr(cp), NULL);
                                                if (!module) {
                                                        return -1;
                                                }
@@ -1971,7 +2084,7 @@ int modules_init(CONF_SECTION *config)
                name = cf_section_name2(subcs);
                if (!name) name = cf_section_name1(subcs);
 
-               module = find_module_instance(modules, name, true);
+               module = module_instantiate(modules, name);
                if (!module) return -1;
        }
        cf_log_info(cs, " } # modules");
@@ -1987,7 +2100,7 @@ int modules_init(CONF_SECTION *config)
  */
 rlm_rcode_t process_authorize(int autz_type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_AUTZ, autz_type, request);
+       return indexed_modcall(MOD_AUTHORIZE, autz_type, request);
 }
 
 /*
@@ -1995,7 +2108,7 @@ rlm_rcode_t process_authorize(int autz_type, REQUEST *request)
  */
 rlm_rcode_t process_authenticate(int auth_type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_AUTH, auth_type, request);
+       return indexed_modcall(MOD_AUTHENTICATE, auth_type, request);
 }
 
 #ifdef WITH_ACCOUNTING
@@ -2004,7 +2117,7 @@ rlm_rcode_t process_authenticate(int auth_type, REQUEST *request)
  */
 rlm_rcode_t module_preacct(REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_PREACCT, 0, request);
+       return indexed_modcall(MOD_PREACCT, 0, request);
 }
 
 /*
@@ -2012,7 +2125,7 @@ rlm_rcode_t module_preacct(REQUEST *request)
  */
 rlm_rcode_t process_accounting(int acct_type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_ACCT, acct_type, request);
+       return indexed_modcall(MOD_ACCOUNTING, acct_type, request);
 }
 #endif
 
@@ -2033,7 +2146,7 @@ int process_checksimul(int sess_type, REQUEST *request, int maxsimul)
        request->simul_max = maxsimul;
        request->simul_mpp = 1;
 
-       rcode = indexed_modcall(RLM_COMPONENT_SESS, sess_type, request);
+       rcode = indexed_modcall(MOD_SESSION, sess_type, request);
 
        if (rcode != RLM_MODULE_OK) {
                /* FIXME: Good spot for a *rate-limited* warning to the log */
@@ -2050,7 +2163,7 @@ int process_checksimul(int sess_type, REQUEST *request, int maxsimul)
  */
 rlm_rcode_t process_pre_proxy(int type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_PRE_PROXY, type, request);
+       return indexed_modcall(MOD_PRE_PROXY, type, request);
 }
 
 /*
@@ -2058,7 +2171,7 @@ rlm_rcode_t process_pre_proxy(int type, REQUEST *request)
  */
 rlm_rcode_t process_post_proxy(int type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_POST_PROXY, type, request);
+       return indexed_modcall(MOD_POST_PROXY, type, request);
 }
 #endif
 
@@ -2067,17 +2180,17 @@ rlm_rcode_t process_post_proxy(int type, REQUEST *request)
  */
 rlm_rcode_t process_post_auth(int postauth_type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_POST_AUTH, postauth_type, request);
+       return indexed_modcall(MOD_POST_AUTH, postauth_type, request);
 }
 
 #ifdef WITH_COA
 rlm_rcode_t process_recv_coa(int recv_coa_type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_RECV_COA, recv_coa_type, request);
+       return indexed_modcall(MOD_RECV_COA, recv_coa_type, request);
 }
 
 rlm_rcode_t process_send_coa(int send_coa_type, REQUEST *request)
 {
-       return indexed_modcall(RLM_COMPONENT_SEND_COA, send_coa_type, request);
+       return indexed_modcall(MOD_SEND_COA, send_coa_type, request);
 }
 #endif
index 80acf57..c893b1e 100644 (file)
@@ -72,7 +72,7 @@ int radius_compare_vps(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *v
 #ifdef HAVE_REGEX
        if ((check->op == T_OP_REG_EQ) || (check->op == T_OP_REG_NE)) {
                ssize_t         slen;
-               regex_t         *preg;
+               regex_t         *preg = NULL;
                regmatch_t      rxmatch[REQUEST_MAX_REGEX + 1]; /* +1 for %{0} (whole match) capture group */
                size_t          nmatch = sizeof(rxmatch) / sizeof(regmatch_t);
 
@@ -95,6 +95,7 @@ int radius_compare_vps(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *v
                        REDEBUG("Error stringifying operand for regular expression");
 
                regex_error:
+                       talloc_free(preg);
                        talloc_free(expr);
                        talloc_free(value);
                        return -2;
@@ -110,14 +111,17 @@ int radius_compare_vps(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *v
                        goto regex_error;
                }
 
-               ret = regex_exec(preg, value_p, talloc_array_length(value_p) - 1, rxmatch, &nmatch);
-               if (ret < 0) {
+               slen = regex_exec(preg, value_p, talloc_array_length(value_p) - 1, rxmatch, &nmatch);
+               if (slen < 0) {
                        RERROR("%s", fr_strerror());
 
-                       return -2;
+                       goto regex_error;
                }
 
                if (check->op == T_OP_REG_EQ) {
+                       /*
+                        *      Add in %{0}. %{1}, etc.
+                        */
                        regex_sub_to_request(request, &preg, value_p, talloc_array_length(value_p) - 1,
                                             rxmatch, nmatch);
                        ret = (slen == 1) ? 0 : -1;
@@ -326,6 +330,49 @@ static bool otherattr(DICT_ATTR const *attribute, DICT_ATTR const **from)
 
 /** Register a function as compare function.
  *
+ * @param name the attribute comparison to register
+ * @param from the attribute we want to compare with. Normally this is the same as attribute.
+ *  If null call the comparison function on every attributes in the request if first_only is false
+ * @param first_only will decide if we loop over the request attributes or stop on the first one
+ * @param func comparison function
+ * @param instance argument to comparison function
+ * @return 0
+ */
+int paircompare_register_byname(char const *name, DICT_ATTR const *from,
+                               bool first_only, RAD_COMPARE_FUNC func, void *instance)
+{
+       ATTR_FLAGS flags;
+       DICT_ATTR const *da;
+
+       memset(&flags, 0, sizeof(flags));
+       flags.compare = 1;
+
+       da = dict_attrbyname(name);
+       if (da) {
+               if (!da->flags.compare) {
+                       fr_strerror_printf("Attribute '%s' already exists.", name);
+                       return -1;
+               }
+       } else if (from) {
+               if (dict_addattr(name, -1, 0, from->type, flags) < 0) {
+                       fr_strerror_printf("Failed creating attribute '%s'", name);
+                       return -1;
+               }
+
+               da = dict_attrbyname(name);
+               if (!da) {
+                       fr_strerror_printf("Failed finding attribute '%s'", name);
+                       return -1;
+               }
+
+               DEBUG("Creating attribute %s", name);
+       }
+
+       return paircompare_register(da, from, first_only, func, instance);
+}
+
+/** Register a function as compare function.
+ *
  * @param attribute to register comparison function for.
  * @param from the attribute we want to compare with. Normally this is the same as attribute.
  *  If null call the comparison function on every attributes in the request if first_only is false
@@ -471,7 +518,7 @@ int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
                                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) {
+                       if (fr_pair_find_by_num(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
                                continue;
                        }
                        break;
@@ -516,7 +563,6 @@ int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
                        return -1;
                }
 
-
                /*
                 *      We've got to xlat the string before doing
                 *      the comparison.
@@ -584,7 +630,7 @@ int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
        return result;
 }
 
-/** Expands an attribute marked with pairmark_xlat
+/** Expands an attribute marked with fr_pair_mark_xlat
  *
  * Writes the new value to the vp.
  *
@@ -616,11 +662,11 @@ int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
         *      then we just want to copy the new value in unmolested.
         */
        if ((vp->op == T_OP_REG_EQ) || (vp->op == T_OP_REG_NE)) {
-               pairstrsteal(vp, expanded);
+               fr_pair_value_strsteal(vp, expanded);
                return 0;
        }
 
-       if (pairparsevalue(vp, expanded, -1) < 0){
+       if (fr_pair_value_from_str(vp, expanded, -1) < 0){
                talloc_free(expanded);
                return -2;
        }
@@ -642,19 +688,19 @@ int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
  * @param[in] vendor number.
  * @return a new VLAUE_PAIR or causes server to exit on error.
  */
-VALUE_PAIR *radius_paircreate(TALLOC_CTX *ctx, VALUE_PAIR **vps,
+VALUE_PAIR *radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps,
                              unsigned int attribute, unsigned int vendor)
 {
        VALUE_PAIR *vp;
 
-       vp = paircreate(ctx, attribute, vendor);
+       vp = fr_pair_afrom_num(ctx, attribute, vendor);
        if (!vp) {
                ERROR("No memory!");
                rad_assert("No memory" == NULL);
                fr_exit_now(1);
        }
 
-       if (vps) pairadd(vps, vp);
+       if (vps) fr_pair_add(vps, vp);
 
        return vp;
 }
@@ -665,7 +711,7 @@ VALUE_PAIR *radius_paircreate(TALLOC_CTX *ctx, VALUE_PAIR **vps,
  */
 void debug_pair(VALUE_PAIR *vp)
 {
-       if (!vp || !debug_flag || !fr_log_fp) return;
+       if (!vp || !rad_debug_lvl || !fr_log_fp) return;
 
        vp_print(fr_log_fp, vp);
 }
@@ -754,7 +800,7 @@ void rdebug_proto_pair_list(log_lvl_t level, REQUEST *request, VALUE_PAIR *vp)
 int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
 {
        int rcode;
-       value_pair_tmpl_t vpt;
+       vp_tmpl_t vpt;
 
        *out = NULL;
 
@@ -780,7 +826,7 @@ int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
 int radius_copy_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, char const *name)
 {
        int rcode;
-       value_pair_tmpl_t vpt;
+       vp_tmpl_t vpt;
 
        *out = NULL;
 
@@ -810,7 +856,7 @@ void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap)
        VALUE_PAIR *vp;
        va_list aq;
 
-       if (!fmt || !request->packet) {
+       if (!fmt || !request || !request->packet) {
                return;
        }
 
@@ -827,11 +873,11 @@ void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap)
        p = talloc_vasprintf(request, fmt, aq);
        va_end(aq);
 
-       MEM(vp = pairmake_packet("Module-Failure-Message", NULL, T_OP_ADD));
+       MEM(vp = pair_make_request("Module-Failure-Message", NULL, T_OP_ADD));
        if (request->module && (request->module[0] != '\0')) {
-               pairsprintf(vp, "%s: %s", request->module, p);
+               fr_pair_value_sprintf(vp, "%s: %s", request->module, p);
        } else {
-               pairsprintf(vp, "%s", p);
+               fr_pair_value_sprintf(vp, "%s", p);
        }
        talloc_free(p);
 }
index 3b6bb96..a0d3c68 100644 (file)
@@ -54,7 +54,14 @@ size_t fr_cond_sprint(char *buffer, size_t bufsize, fr_cond_t const *in)
        char *end = buffer + bufsize - 1;
        fr_cond_t const *c = in;
 
+       rad_assert(bufsize > 0);
+
 next:
+       if (!c) {
+               p[0] = '\0';
+               return 0;
+       }
+
        if (c->negate) {
                *(p++) = '!';   /* FIXME: only allow for child? */
        }
@@ -175,9 +182,15 @@ static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char **out,  char cons
                        if (cf_new_escape) {
                                ssize_t slen;
                                value_data_t data;
+                               char quote = *start;
                                PW_TYPE src_type = PW_TYPE_STRING;
 
-                               slen = value_data_from_str(ctx, &data, &src_type, NULL, start + 1, p - (start + 1), *start);
+                               /*
+                                *      Regex compilers can handle escapes.  So we don't do it.
+                                */
+                               if (quote == '/') quote = '\0';
+
+                               slen = value_data_from_str(ctx, &data, &src_type, NULL, start + 1, p - (start + 1), quote);
                                if (slen < 0) {
                                        *error = "error parsing string";
                                        return slen - 1;
@@ -186,9 +199,17 @@ static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char **out,  char cons
                                talloc_free(*out);
                                *out = talloc_steal(ctx, data.ptr);
                                data.strvalue = NULL;
-
                        } else {
-                               *q = '\0'; /* terminate the output string */
+                               char *out2;
+
+                               *(q++) = '\0'; /* terminate the output string */
+
+                               out2 = talloc_realloc(ctx, *out, char, (q - *out));
+                               if (!out2) {
+                                       *error = "Out of memory";
+                                       return -1;
+                               }
+                               *out = out2;
                        }
 
                        p++;
@@ -352,6 +373,73 @@ static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda,
        return q - start;
 }
 
+static bool condition_check_types(fr_cond_t *c, PW_TYPE lhs_type)
+{
+       /*
+        *      SOME integer mismatch is OK.  If the LHS has a large type,
+        *      and the RHS has a small type, it's OK.
+        *
+        *      If the LHS has a small type, and the RHS has a large type,
+        *      then add a cast to the LHS.
+        */
+       if (lhs_type == PW_TYPE_INTEGER64) {
+               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER) ||
+                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_SHORT) ||
+                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_BYTE)) {
+                       c->cast = NULL;
+                       return true;
+               }
+       }
+
+       if (lhs_type == PW_TYPE_INTEGER) {
+               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_SHORT) ||
+                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_BYTE)) {
+                       c->cast = NULL;
+                       return true;
+               }
+
+               if (c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER64) {
+                       c->cast = c->data.map->rhs->tmpl_da;
+                       return true;
+               }
+       }
+
+       if (lhs_type == PW_TYPE_SHORT) {
+               if (c->data.map->rhs->tmpl_da->type == PW_TYPE_BYTE) {
+                       c->cast = NULL;
+                       return true;
+               }
+
+               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER64) ||
+                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER)) {
+                       c->cast = c->data.map->rhs->tmpl_da;
+                       return true;
+               }
+       }
+
+       if (lhs_type == PW_TYPE_BYTE) {
+               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER64) ||
+                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER) ||
+                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_SHORT)) {
+                       c->cast = c->data.map->rhs->tmpl_da;
+                       return true;
+               }
+       }
+
+       if ((lhs_type == PW_TYPE_IPV4_PREFIX) &&
+           (c->data.map->rhs->tmpl_da->type == PW_TYPE_IPV4_ADDR)) {
+               return true;
+       }
+
+       if ((lhs_type == PW_TYPE_IPV6_PREFIX) &&
+           (c->data.map->rhs->tmpl_da->type == PW_TYPE_IPV6_ADDR)) {
+               return true;
+       }
+
+       return false;
+}
+
+
 /*
  *     Less code means less bugs
  */
@@ -516,7 +604,8 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                        c->type = COND_TYPE_EXISTS;
                        c->ci = ci;
 
-                       tlen = tmpl_afrom_str(c, &c->data.vpt, lhs, talloc_array_length(lhs) - 1, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                       tlen = tmpl_afrom_str(c, &c->data.vpt, lhs, talloc_array_length(lhs) - 1,
+                                             lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST, false);
                        if (tlen < 0) {
                                p = lhs_p - tlen;
                                return_P(fr_strerror());
@@ -534,7 +623,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                        bool iflag = false;
                        bool mflag = false;
 #endif
-                       value_pair_map_t *map;
+                       vp_map_t *map;
 
                        /*
                         *      The next thing should now be a comparison operator.
@@ -702,15 +791,16 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                         *      want to separate parse errors in the
                         *      LHS from ones in the RHS.
                         */
-                       c->data.map = map = talloc_zero(c, value_pair_map_t);
+                       c->data.map = map = talloc_zero(c, vp_map_t);
 
-                       tlen = tmpl_afrom_str(map, &map->lhs, lhs, talloc_array_length(lhs) - 1, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                       tlen = tmpl_afrom_str(map, &map->lhs, lhs, talloc_array_length(lhs) - 1,
+                                             lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST, false);
                        if (tlen < 0) {
                                p = lhs_p - tlen;
                                return_P(fr_strerror());
                        }
 
-                       if (!tmpl_define_unknown_attr(map->lhs)) {
+                       if (tmpl_define_unknown_attr(map->lhs) < 0) {
                                return_lhs("Failed defining attribute");
                        return_lhs:
                                if (lhs) talloc_free(lhs);
@@ -747,15 +837,16 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                            map->lhs->tmpl_da->flags.is_unknown &&
                            map_cast_from_hex(map, rhs_type, rhs)) {
                                /* do nothing */
+
                        } else {
                                tlen = tmpl_afrom_str(map, &map->rhs, rhs, talloc_array_length(rhs) - 1, rhs_type,
-                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST, false);
                                if (tlen < 0) {
                                        p = rhs_p - tlen;
                                        return_P(fr_strerror());
                                }
 
-                               if (!tmpl_define_unknown_attr(map->rhs)) {
+                               if (tmpl_define_unknown_attr(map->rhs) < 0) {
                                        return_rhs("Failed defining attribute");
                                }
                        }
@@ -798,6 +889,10 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                        if (c->cast) {
                                if ((c->data.map->rhs->type == TMPL_TYPE_ATTR) &&
                                    (c->cast->type != c->data.map->rhs->tmpl_da->type)) {
+                                       if (condition_check_types(c, c->cast->type)) {
+                                               goto keep_going;
+                                       }
+
                                        goto same_type;
                                }
 
@@ -812,7 +907,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->lhs->type == TMPL_TYPE_LITERAL) &&
-                                   !tmpl_cast_in_place(c->data.map->lhs, c->cast->type, c->cast)) {
+                                   (tmpl_cast_in_place(c->data.map->lhs, c->cast->type, c->cast) < 0)) {
                                        *error = "Failed to parse field";
                                        if (lhs) talloc_free(lhs);
                                        if (rhs) talloc_free(rhs);
@@ -826,7 +921,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 */
                                if ((c->data.map->lhs->type == TMPL_TYPE_DATA) &&
                                    (c->data.map->rhs->type == TMPL_TYPE_LITERAL) &&
-                                   !tmpl_cast_in_place(c->data.map->rhs, c->cast->type, c->cast)) {
+                                   (tmpl_cast_in_place(c->data.map->rhs, c->cast->type, c->cast) < 0)) {
                                        return_rhs("Failed to parse field");
                                }
 
@@ -909,59 +1004,16 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                }
 
                        } else {
+                               vp_tmpl_t *vpt;
+
                                /*
                                 *      Two attributes?  They must be of the same type
                                 */
                                if ((c->data.map->rhs->type == TMPL_TYPE_ATTR) &&
                                    (c->data.map->lhs->type == TMPL_TYPE_ATTR) &&
                                    (c->data.map->lhs->tmpl_da->type != c->data.map->rhs->tmpl_da->type)) {
-
-                                       /*
-                                        *      SOME integer mismatch is OK.  If the LHS has a large type,
-                                        *      and the RHS has a small type, it's OK.
-                                        *
-                                        *      If the LHS has a small type, and the RHS has a large type,
-                                        *      then add a cast to the LHS.
-                                        */
-                                       if (c->data.map->lhs->tmpl_da->type == PW_TYPE_INTEGER64) {
-                                               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER) ||
-                                                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_SHORT) ||
-                                                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_BYTE)) {
-                                                       goto keep_going;
-                                               }
-                                       }
-
-                                       if (c->data.map->lhs->tmpl_da->type == PW_TYPE_INTEGER) {
-                                               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_SHORT) ||
-                                                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_BYTE)) {
-                                                       goto keep_going;
-                                               }
-
-                                               if (c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER64) {
-                                                       c->cast = c->data.map->rhs->tmpl_da;
-                                                       goto keep_going;
-                                               }
-                                       }
-
-                                       if (c->data.map->lhs->tmpl_da->type == PW_TYPE_SHORT) {
-                                               if (c->data.map->rhs->tmpl_da->type == PW_TYPE_BYTE) {
-                                                       goto keep_going;
-                                               }
-
-                                               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER64) ||
-                                                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER)) {
-                                                       c->cast = c->data.map->rhs->tmpl_da;
-                                                       goto keep_going;
-                                               }
-                                       }
-
-                                       if (c->data.map->lhs->tmpl_da->type == PW_TYPE_BYTE) {
-                                               if ((c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER64) ||
-                                                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_INTEGER) ||
-                                                   (c->data.map->rhs->tmpl_da->type == PW_TYPE_SHORT)) {
-                                                       c->cast = c->data.map->rhs->tmpl_da;
-                                                       goto keep_going;
-                                               }
+                                       if (condition_check_types(c, c->data.map->lhs->tmpl_da->type)) {
+                                               goto keep_going;
                                        }
 
                                same_type:
@@ -1022,7 +1074,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->rhs->type == TMPL_TYPE_LITERAL) &&
-                                   !tmpl_cast_in_place(c->data.map->rhs, c->cast->type, c->cast)) {
+                                   (tmpl_cast_in_place(c->data.map->rhs, c->cast->type, c->cast) < 0)) {
                                        return_rhs("Failed to parse field");
                                }
 
@@ -1053,7 +1105,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                                break;
                                        }
 
-                                       if (!tmpl_cast_in_place(c->data.map->rhs, type, c->data.map->lhs->tmpl_da)) {
+                                       if (tmpl_cast_in_place(c->data.map->rhs, type, c->data.map->lhs->tmpl_da) < 0) {
                                                DICT_ATTR const *da = c->data.map->lhs->tmpl_da;
 
                                                if ((da->vendor == 0) &&
@@ -1094,8 +1146,59 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                                }
                                                c->data.map->lhs->tmpl_da = da;
                                        }
+                               } /* attr to literal comparison */
+
+                               /*
+                                *      If the LHS is a bare word, AND it looks like
+                                *      an attribute, try to parse it as such.
+                                *
+                                *      This allows LDAP-Group and SQL-Group to work.
+                                *
+                                *      The real fix is to just read the config files,
+                                *      and do no parsing until after all of the modules
+                                *      are loaded.  But that has issues, too.
+                                */
+                               if ((c->data.map->lhs->type == TMPL_TYPE_LITERAL) &&
+                                   (lhs_type == T_BARE_WORD) &&
+                                   (c->data.map->rhs->type == TMPL_TYPE_LITERAL)) {
+                                       int hyphens = 0;
+                                       bool may_be_attr = true;
+                                       size_t i;
+                                       ssize_t attr_slen;
+
+                                       /*
+                                        *      Backwards compatibility: Allow Foo-Bar,
+                                        *      e.g. LDAP-Group and SQL-Group.
+                                        */
+                                       for (i = 0; i < c->data.map->lhs->len; i++) {
+                                               if (!dict_attr_allowed_chars[(unsigned char) c->data.map->lhs->name[i]]) {
+                                                       may_be_attr = false;
+                                                       break;
+                                               }
+
+                                               if (c->data.map->lhs->name[i] == '-') {
+                                                       hyphens++;
+                                                       if (hyphens > 1) {
+                                                               may_be_attr = false;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+
+                                       if (!hyphens || (hyphens > 3)) may_be_attr = false;
+
+                                       if (may_be_attr) {
+                                               attr_slen = tmpl_afrom_attr_str(c->data.map, &vpt, lhs,
+                                                                               REQUEST_CURRENT, PAIR_LIST_REQUEST,
+                                                                               true, true);
+                                               if ((attr_slen > 0) && (vpt->len == c->data.map->lhs->len)) {
+                                                       talloc_free(c->data.map->lhs);
+                                                       c->data.map->lhs = vpt;
+                                                       c->pass2_fixup = PASS2_FIXUP_ATTR;
+                                               }
+                                       }
                                }
-                       }
+                       } /* we didn't have a cast */
 
                keep_going:
                        p += slen;
@@ -1271,7 +1374,7 @@ done:
                 */
                if ((c->data.map->op == T_OP_CMP_TRUE) ||
                    (c->data.map->op == T_OP_CMP_FALSE)) {
-                       value_pair_tmpl_t *vpt;
+                       vp_tmpl_t *vpt;
 
                        vpt = talloc_steal(c, c->data.map->lhs);
                        c->data.map->lhs = NULL;
@@ -1348,14 +1451,15 @@ done:
                }
 
                /*
-                *      <ipaddr>"foo" CMP &Attribute-Name The cast is
-                *      unnecessary, and we can re-write it so that
-                *      the attribute reference is on the LHS.
+                *      <ipaddr>"foo" CMP &Attribute-Name The cast may
+                *      not be necessary, and we can re-write it so
+                *      that the attribute reference is on the LHS.
                 */
                if (c->cast &&
                    (c->data.map->rhs->type == TMPL_TYPE_ATTR) &&
+                   (c->cast->type == c->data.map->rhs->tmpl_da->type) &&
                    (c->data.map->lhs->type != TMPL_TYPE_ATTR)) {
-                       value_pair_tmpl_t *tmp;
+                       vp_tmpl_t *tmp;
 
                        tmp = c->data.map->rhs;
                        c->data.map->rhs = c->data.map->lhs;
index 881d462..14cc3b0 100644 (file)
@@ -63,7 +63,6 @@ static char const *action_codes[] = {
        "run",
        "done",
        "dup",
-       "conflicting",
        "timer",
 #ifdef WITH_PROXY
        "proxy-reply"
@@ -71,11 +70,16 @@ static char const *action_codes[] = {
 };
 
 #ifdef DEBUG_STATE_MACHINE
-#define TRACE_STATE_MACHINE if (debug_flag) do { struct timeval debug_tv; \
-                                                gettimeofday(&debug_tv, NULL);\
-                                                debug_tv.tv_sec -= fr_start_time;\
-                                                printf("(%u) %d.%06d ********\tSTATE %s action %s live M-%s C-%s\t********\n",\
-                                                       request->number, (int) debug_tv.tv_sec, (int) debug_tv.tv_usec,  __FUNCTION__, action_codes[action], master_state_names[request->master_state], child_state_names[request->child_state]); } while (0)
+#  define TRACE_STATE_MACHINE \
+if (rad_debug_lvl) do { \
+       struct timeval debug_tv; \
+       gettimeofday(&debug_tv, NULL); \
+       debug_tv.tv_sec -= fr_start_time; \
+       printf("(%u) %d.%06d ********\tSTATE %s action %s live M-%s C-%s\t********\n",\
+              request->number, (int) debug_tv.tv_sec, (int) debug_tv.tv_usec, \
+              __FUNCTION__, action_codes[action], master_state_names[request->master_state], \
+              child_state_names[request->child_state]); \
+} while (0)
 
 static char const *master_state_names[REQUEST_MASTER_NUM_STATES] = {
        "?",
@@ -95,18 +99,49 @@ static char const *child_state_names[REQUEST_CHILD_NUM_STATES] = {
 };
 
 #else
-#define TRACE_STATE_MACHINE {}
+#  define TRACE_STATE_MACHINE {}
 #endif
 
-/*
- *     Declare a state in the state machine.
+static NEVER_RETURNS void _rad_panic(char const *file, unsigned int line, char const *msg)
+{
+       ERROR("%s[%u]: %s", file, line, msg);
+       fr_exit_now(1);
+}
+
+#define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)
+
+/** Declare a state in the state machine
+ *
+ * Expands to the start of a function definition for a given state.
  *
+ * @param _x the name of the state.
  */
-#define STATE_MACHINE_DECL(_x) static void CC_HINT(nonnull) _x(REQUEST *request, int action)
+#define STATE_MACHINE_DECL(_x) static void _x(REQUEST *request, int action)
+
+static void request_timer(void *ctx);
+
+/** Insert #REQUEST back into the event heap, to continue executing at a future time
+ *
+ * @param file the state machine timer call occurred in.
+ * @param line the state machine timer call occurred on.
+ * @param request to set add the timer event for.
+ * @param when the event should fine.
+ * @param action to perform when we resume processing the request.
+ */
+static inline void state_machine_timer(char const *file, int line, REQUEST *request,
+                                      struct timeval *when, fr_state_action_t action)
+{
+       request->timer_action = action;
+       if (!fr_event_insert(el, request_timer, request, when, &request->ev)) {
+               _rad_panic(file, line, "Failed to insert event");
+       }
+}
 
-#define STATE_MACHINE_TIMER(_x) request->timer_action = _x; \
-               fr_event_insert(el, request_timer, request, \
-                               &when, &request->ev);
+/** @copybrief state_machine_timer
+ *
+ * @param _x the action to perform when we resume processing the request.
+ */
+#define STATE_MACHINE_TIMER(_x) state_machine_timer(__FILE__, __LINE__, request, &when, _x)
 
 /*
  *     We need a different VERIFY_REQUEST macro in process.c
@@ -195,34 +230,33 @@ static char const *child_state_names[REQUEST_CHILD_NUM_STATES] = {
  *     being called from a child thread or the master, and then do
  *     different things based on that.
  */
-
-
 #ifdef WITH_PROXY
 static fr_packet_list_t *proxy_list = NULL;
+static TALLOC_CTX *proxy_ctx = NULL;
 #endif
 
 #ifdef HAVE_PTHREAD_H
-#ifdef WITH_PROXY
+#  ifdef WITH_PROXY
 static pthread_mutex_t proxy_mutex;
 static bool proxy_no_new_sockets = false;
-#endif
+#  endif
 
-#define PTHREAD_MUTEX_LOCK if (spawn_flag) pthread_mutex_lock
-#define PTHREAD_MUTEX_UNLOCK if (spawn_flag) pthread_mutex_unlock
+#  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
+#  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
+#  define PTHREAD_MUTEX_LOCK(_x)
+#  define PTHREAD_MUTEX_UNLOCK(_x)
+#  define NO_CHILD_THREAD
 #endif
 
-#if  defined(HAVE_PTHREAD_H) && !defined (NDEBUG)
+#ifdef HAVE_PTHREAD_H
 static bool we_are_master(void)
 {
        if (spawn_flag &&
@@ -232,13 +266,31 @@ static bool we_are_master(void)
 
        return true;
 }
-#define ASSERT_MASTER  if (!we_are_master()) rad_panic("We are not master")
 
+/*
+ *     Assertions are debug checks.
+ */
+#  ifndef NDEBUG
+#    define ASSERT_MASTER      if (!we_are_master()) rad_panic("We are not master")
+#    endif
 #else
-#define we_are_master(_x) (1)
-#define ASSERT_MASTER
+
+/*
+ *     No threads: we're always master.
+ */
+#  define we_are_master(_x) (1)
+#endif /* HAVE_PTHREAD_H */
+
+#ifndef ASSERT_MASTER
+#  define ASSERT_MASTER
 #endif
 
+/*
+ *     Make state transitions simpler.
+ */
+#define FINAL_STATE(_x) NO_CHILD_THREAD; request->component = "<" #_x ">"; request->module = ""; request->child_state = _x
+
+
 static int event_new_fd(rad_listen_t *this);
 
 /*
@@ -249,8 +301,8 @@ static int event_new_fd(rad_listen_t *this);
 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
+#  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)
 {
@@ -291,54 +343,53 @@ void radius_update_listener(rad_listen_t *this)
 /*
  *     This is easier than ifdef's throughout the code.
  */
-#define FD_MUTEX_LOCK(_x)
-#define FD_MUTEX_UNLOCK(_x)
+#  define FD_MUTEX_LOCK(_x)
+#  define FD_MUTEX_UNLOCK(_x)
 #endif
 
 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, 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);
+static int request_will_proxy(REQUEST *request) CC_HINT(nonnull);
+static int request_proxy(REQUEST *request, int retransmit) CC_HINT(nonnull);
+STATE_MACHINE_DECL(request_ping) CC_HINT(nonnull);
+
+STATE_MACHINE_DECL(request_response_delay) CC_HINT(nonnull);
+STATE_MACHINE_DECL(request_cleanup_delay) CC_HINT(nonnull);
+STATE_MACHINE_DECL(request_running) CC_HINT(nonnull);
+STATE_MACHINE_DECL(request_done) CC_HINT(nonnull);
+
+STATE_MACHINE_DECL(proxy_no_reply) CC_HINT(nonnull);
+STATE_MACHINE_DECL(proxy_running) CC_HINT(nonnull);
+STATE_MACHINE_DECL(proxy_wait_for_reply) CC_HINT(nonnull);
+
+static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply) CC_HINT(nonnull (1));
+static void remove_from_proxy_hash(REQUEST *request) CC_HINT(nonnull);
+static void remove_from_proxy_hash_nl(REQUEST *request, bool yank) CC_HINT(nonnull);
+static int insert_into_proxy_hash(REQUEST *request) CC_HINT(nonnull);
 #endif
 
 static REQUEST *request_setup(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PACKET *packet,
                              RADCLIENT *client, RAD_REQUEST_FUNP fun);
+static int request_pre_handler(REQUEST *request, UNUSED int action) CC_HINT(nonnull);
 
-STATE_MACHINE_DECL(request_common);
-STATE_MACHINE_DECL(request_response_delay);
-STATE_MACHINE_DECL(request_cleanup_delay);
-STATE_MACHINE_DECL(request_running);
 #ifdef WITH_COA
-static void request_coa_originate(REQUEST *request);
-STATE_MACHINE_DECL(coa_running);
-STATE_MACHINE_DECL(coa_wait_for_reply);
-STATE_MACHINE_DECL(coa_no_reply);
-STATE_MACHINE_DECL(coa_separate);
+static void request_coa_originate(REQUEST *request) CC_HINT(nonnull);
+STATE_MACHINE_DECL(coa_wait_for_reply) CC_HINT(nonnull);
+STATE_MACHINE_DECL(coa_no_reply) CC_HINT(nonnull);
+STATE_MACHINE_DECL(coa_running) CC_HINT(nonnull);
+static void coa_separate(REQUEST *request) CC_HINT(nonnull);
+#  define COA_SEPARATE if (request->coa) coa_separate(request->coa);
+#else
+#  define COA_SEPARATE
 #endif
 
+#define CHECK_FOR_STOP do { if (request->master_state == REQUEST_STOP_PROCESSING) {request_done(request, FR_ACTION_DONE);return;}} while (0)
+
 #undef USEC
 #define USEC (1000000)
 
 #define INSERT_EVENT(_function, _ctx) if (!fr_event_insert(el, _function, _ctx, &((_ctx)->when), &((_ctx)->ev))) { _rad_panic(__FILE__, __LINE__, "Failed to insert event"); }
 
-static NEVER_RETURNS void _rad_panic(char const *file, unsigned int line, char const *msg)
-{
-       ERROR("[%s:%d] %s", file, line, msg);
-#ifndef NDEBUG
-       rad_assert(0 == 1);
-#endif
-       fr_exit(1);
-}
-
-#define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)
-
 static void tv_add(struct timeval *tv, int usec_delay)
 {
        if (usec_delay >= USEC) {
@@ -371,31 +422,39 @@ static void debug_packet(REQUEST *request, RADIUS_PACKET *packet, bool received)
         *      This really belongs in a utility library
         */
        if (is_radius_code(packet->code)) {
-               RDEBUG("%s %s Id %i from %s:%i to %s:%i length %zu",
+               RDEBUG("%s %s Id %i from %s%s%s:%i to %s%s%s:%i length %zu",
                       received ? "Received" : "Sent",
                       fr_packet_codes[packet->code],
                       packet->id,
+                      packet->src_ipaddr.af == AF_INET6 ? "[" : "",
                       inet_ntop(packet->src_ipaddr.af,
                                 &packet->src_ipaddr.ipaddr,
                                 src_ipaddr, sizeof(src_ipaddr)),
+                      packet->src_ipaddr.af == AF_INET6 ? "]" : "",
                       packet->src_port,
+                      packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
                       inet_ntop(packet->dst_ipaddr.af,
                                 &packet->dst_ipaddr.ipaddr,
                                 dst_ipaddr, sizeof(dst_ipaddr)),
+                      packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
                       packet->dst_port,
                       packet->data_len);
        } else {
-               RDEBUG("%s code %i Id %i from %s:%i to %s:%i length %zu",
+               RDEBUG("%s code %u Id %i from %s%s%s:%i to %s%s%s:%i length %zu\n",
                       received ? "Received" : "Sent",
                       packet->code,
                       packet->id,
+                      packet->src_ipaddr.af == AF_INET6 ? "[" : "",
                       inet_ntop(packet->src_ipaddr.af,
                                 &packet->src_ipaddr.ipaddr,
                                 src_ipaddr, sizeof(src_ipaddr)),
+                      packet->src_ipaddr.af == AF_INET6 ? "]" : "",
                       packet->src_port,
+                      packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
                       inet_ntop(packet->dst_ipaddr.af,
                                 &packet->dst_ipaddr.ipaddr,
                                 dst_ipaddr, sizeof(dst_ipaddr)),
+                      packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
                       packet->dst_port,
                       packet->data_len);
        }
@@ -483,42 +542,63 @@ static void request_timer(void *ctx)
  */
 static void request_free(REQUEST *request)
 {
-       void *ptr = talloc_parent(request);
+       void *ptr;
 
-       if (ptr) {
-               talloc_free(ptr);
-       } else {
+       rad_assert(request->ev == NULL);
+       rad_assert(!request->in_request_hash);
+       rad_assert(!request->in_proxy_hash);
+
+       if ((request->options & RAD_REQUEST_OPTION_CTX) == 0) {
                talloc_free(request);
+               return;
        }
+
+       ptr = talloc_parent(request);
+       rad_assert(ptr != NULL);
+       talloc_free(ptr);
 }
 
 
-/*
- *     Only ever called from the master thread.
- */
-STATE_MACHINE_DECL(request_done)
-{
-       struct timeval now, when;
 #ifdef WITH_PROXY
+static void proxy_reply_too_late(REQUEST *request)
+{
        char buffer[128];
+
+       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);
+}
 #endif
 
+
+/** Mark a request DONE and clean it up.
+ *
+ *  When a request is DONE, it can have ties to a number of other
+ *  portions of the server.  The request hash, proxy hash, events,
+ *  child threads, etc.  This function takes care of either cleaning
+ *  up the request, or managing the timers to wait for the ties to be
+ *  removed.
+ *
+ *  \dot
+ *     digraph done {
+ *             done -> done [ label = "still running" ];
+ *     }
+ *  \enddot
+ */
+static void request_done(REQUEST *request, int action)
+{
+       struct timeval now, when;
+
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
 
-#ifdef WITH_COA
        /*
-        *      CoA requests can be cleaned up in the child thread,
-        *      but ONLY if they aren't tied into anything.
+        *      Force this no matter what.
         */
-       if (request->parent && (request->parent->coa == request)) {
-               rad_assert(!request->in_request_hash);
-               rad_assert(!request->in_proxy_hash);
-               rad_assert(action == FR_ACTION_DONE);
-               rad_assert(request->ev == NULL);
-       }
-#endif
+       request->process = request_done;
 
 #ifdef WITH_DETAIL
        /*
@@ -539,20 +619,24 @@ STATE_MACHINE_DECL(request_done)
         *      and wait for the master thread timer to clean us up.
         */
        if (!we_are_master()) {
-               request->child_state = REQUEST_DONE;
-               NO_CHILD_THREAD;
+               FINAL_STATE(REQUEST_DONE);
                return;
        }
 #endif
 
+       /*
+        *      Mark the request as STOP.
+        */
+       request->master_state = REQUEST_STOP_PROCESSING;
+
 #ifdef WITH_COA
        /*
         *      Move the CoA request to its own handler.
         */
        if (request->coa) {
-               coa_separate(request->coa, FR_ACTION_TIMER);
+               coa_separate(request->coa);
        } else if (request->parent && (request->parent->coa == request)) {
-               coa_separate(request, FR_ACTION_TIMER);
+               coa_separate(request);
        }
 #endif
 
@@ -575,53 +659,20 @@ STATE_MACHINE_DECL(request_done)
                break;
 
                /*
-                *      This is only called from the master thread
-                *      when there is a child thread processing the
-                *      request.
-                */
-       case FR_ACTION_CONFLICTING:
-               if (request->child_state == REQUEST_DONE) break;
-
-               /*
-                *      If there's a reply packet, then we presume
-                *      that the child has sent the reply, and we get
-                *      pinged here before the child has a chance to
-                *      say "I'm done!"
-                */
-               if (request->reply->data) break;
-
-               RERROR("Received conflicting packet from "
-                              "client %s port %d - ID: %u due to "
-                              "unfinished request.  Giving up on old request.",
-                              request->client->shortname,
-                              request->packet->src_port, request->packet->id);
-               break;
-
-               /*
-                *      Called only when there's an error remembering
-                *      the packet, or when the socket gets closed from
-                *      under us.
+                *      Mark the request as done.
                 */
        case FR_ACTION_DONE:
 #ifdef HAVE_PTHREAD_H
                /*
-                *      Do NOT set child_state to DONE if it's still in the queue.
-                */
-               if (we_are_master() && (request->child_state == REQUEST_QUEUED)) {
-                       break;
-               }
-
-               /*
-                *      If we have child threads and we're NOT the
-                *      thread handling the request, don't do anything.
+                *      If the child is still running, leave it alone.
                 */
-               if (spawn_flag &&
-                   !pthread_equal(pthread_self(), request->child_pid)) {
+               if (spawn_flag && (request->child_state <= REQUEST_RUNNING)) {
                        break;
                }
 #endif
+
 #ifdef DEBUG_STATE_MACHINE
-               if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
+               if (rad_debug_lvl) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
                                       request->number, __FUNCTION__,
                                       child_state_names[request->child_state],
                                       child_state_names[REQUEST_DONE]);
@@ -638,21 +689,12 @@ STATE_MACHINE_DECL(request_done)
                break;
 
 #ifdef WITH_PROXY
-               /*
-                *      Child is still alive, and we're receiving more
-                *      packets from the home server.
-                */
        case FR_ACTION_PROXY_REPLY:
-               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;
+               proxy_reply_too_late(request);
+               break;
 #endif
 
        default:
-               RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
                break;
        }
 
@@ -660,7 +702,6 @@ STATE_MACHINE_DECL(request_done)
         *      Remove it from the request hash.
         */
        if (request->in_request_hash) {
-               ASSERT_MASTER;
                if (!rbtree_deletebydata(pl, &request->packet)) {
                        rad_assert(0 == 1);
                }
@@ -711,24 +752,17 @@ STATE_MACHINE_DECL(request_done)
        /*
         *      If there's no children, we can mark the request as done.
         */
-       if (!spawn_flag) {
-               request->child_state = REQUEST_DONE;
-       }
+       if (!spawn_flag) request->child_state = REQUEST_DONE;
 #endif
 
-       if (request->child_state != REQUEST_DONE) {
+       /*
+        *      If the child is still running, wait for it to be finished.
+        */
+       if (request->child_state <= REQUEST_RUNNING) {
                gettimeofday(&now, NULL);
 #ifdef WITH_PROXY
        wait_some_more:
 #endif
-
-#ifdef HAVE_PTHREAD_H
-               if (spawn_flag &&
-                   (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0)) {
-                       RDEBUG("Waiting for child thread to stop");
-               }
-#endif
-
                when = now;
                if (request->delay < (USEC / 3)) request->delay = USEC / 3;
                tv_add(&when, request->delay);
@@ -755,8 +789,7 @@ STATE_MACHINE_DECL(request_done)
                 *      If we're the last one, remove the listener now.
                 */
                if ((request->listener->count == 0) &&
-                   (request->listener->status == RAD_LISTEN_STATUS_EOL)) {
-                       request->listener->status = RAD_LISTEN_STATUS_REMOVE_NOW;
+                   (request->listener->status >= RAD_LISTEN_STATUS_FROZEN)) {
                        event_new_fd(request->listener);
                }
        }
@@ -768,27 +801,44 @@ STATE_MACHINE_DECL(request_done)
                        (unsigned int) (request->timestamp - fr_start_time));
        } /* else don't print anything */
 
-       if (request->ev) fr_event_delete(el, &request->ev);
-
+       ASSERT_MASTER;
+       fr_event_delete(el, &request->ev);
        request_free(request);
 }
 
 
-static void request_cleanup_delay_init(REQUEST *request, struct timeval const *pnow)
+static void request_cleanup_delay_init(REQUEST *request)
 {
        struct timeval now, when;
 
        VERIFY_REQUEST(request);
 
+       /*
+        *      Do cleanup delay ONLY for RADIUS packets from a real
+        *      client.  Everything else just gets cleaned up
+        *      immediately.
+        */
+       if (request->packet->dst_port == 0) goto done;
+
+       /*
+        *      Accounting packets shouldn't be retransmitted.  They
+        *      should always be updated with Acct-Delay-Time.
+        */
+#ifdef WITH_ACCOUNTING
        if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) goto done;
+#endif
+
+#ifdef WITH_DHCP
+       if (request->listener->type == RAD_LISTEN_DHCP) goto done;
+#endif
+
+#ifdef WITH_VMPS
+       if (request->listener->type == RAD_LISTEN_VQP) goto done;
+#endif
 
        if (!request->root->cleanup_delay) goto done;
 
-       if (pnow) {
-               now = *pnow;
-       } else {
-               gettimeofday(&now, NULL);
-       }
+       gettimeofday(&now, NULL);
 
        rad_assert(request->reply->timestamp.tv_sec != 0);
        when = request->reply->timestamp;
@@ -801,10 +851,22 @@ static void request_cleanup_delay_init(REQUEST *request, struct timeval const *p
         */
        if (timercmp(&when, &now, >)) {
 #ifdef DEBUG_STATE_MACHINE
-               if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
+               if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
 #endif
                request->process = request_cleanup_delay;
-               request->child_state = REQUEST_DONE;
+
+               if (!we_are_master()) {
+                       FINAL_STATE(REQUEST_CLEANUP_DELAY);
+                       return;
+               }
+
+               /*
+                *      Update this if we can, otherwise let the timers pick it up.
+                */
+               request->child_state = REQUEST_CLEANUP_DELAY;
+#ifdef HAVE_PTHREAD_H
+               rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
+#endif
                STATE_MACHINE_TIMER(FR_ACTION_TIMER);
                return;
        }
@@ -818,9 +880,9 @@ done:
 
 
 /*
- *     Function to do all time-related events.
+ *     Enforce max_request_time.
  */
-static void request_process_timer(REQUEST *request)
+static bool request_max_time(REQUEST *request)
 {
        struct timeval now, when;
        rad_assert(request->magic == REQUEST_MAGIC);
@@ -833,208 +895,60 @@ static void request_process_timer(REQUEST *request)
        TRACE_STATE_MACHINE;
        ASSERT_MASTER;
 
-#ifdef WITH_COA
-       /*
-        *      If we originated a CoA request, divorce it from the
-        *      parent.  Then, set up the timers so that we can clean
-        *      it up as appropriate.
-        */
-       if (request->coa) coa_separate(request->coa, FR_ACTION_TIMER);
-
        /*
-        *      If we're the request, OR it isn't originating a CoA
-        *      request, check more things.
+        *      The child thread has acknowledged it's done.
+        *      Transition to the DONE state.
+        *
+        *      If the request was marked STOP, then the "check for
+        *      stop" macro already took care of it.
         */
-       if (!request->proxy || (request->packet->code == request->proxy->code))
-#endif
-       {
-               rad_assert(request->listener != NULL);
-
-               /*
-                *      The socket was closed.  Tell the request that
-                *      there is no point in continuing.
-                */
-               if (request->listener->status != RAD_LISTEN_STATUS_KNOWN) {
-                       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;
-                       }
-               }
+       if (request->child_state == REQUEST_DONE) {
+       done:
+               request_done(request, FR_ACTION_DONE);
+               return true;
        }
 
-       gettimeofday(&now, NULL);
-
        /*
-        *      The request was forcibly stopped.
+        *      The request is still running.  Enforce max_request_time.
         */
-       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;
-               }
-       }
-
-       rad_assert(request->master_state == REQUEST_ACTIVE);
+       fr_event_now(el, &now);
+       when = request->packet->timestamp;
+       when.tv_sec += request->root->max_request_time;
 
        /*
-        *      It's still supposed to be running.
+        *      Taking too long: tell it to die.
         */
-       switch (request->child_state) {
-       case REQUEST_QUEUED:
-       case REQUEST_RUNNING:
-               when = request->packet->timestamp;
-               when.tv_sec += request->root->max_request_time;
-
-               /*
-                *      Taking too long: tell it to die.
-                */
-               if (timercmp(&now, &when, >=)) {
+       if (timercmp(&now, &when, >=)) {
 #ifdef HAVE_PTHREAD_H
-                       /*
-                        *      If there's a child thread processing it,
-                        *      complain.
-                        */
-                       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>");
-                               exec_trigger(request, NULL, "server.thread.unresponsive", true);
-                       }
-#endif
-                       request->master_state = REQUEST_STOP_PROCESSING;
-               }
-               goto delay;     /* sleep some more */
-
-#ifdef WITH_PROXY
-       case REQUEST_PROXIED:
-               when = request->packet->timestamp;
-               when.tv_sec += request->root->max_request_time;
-
-               if (timercmp(&now, &when, >=)) {
-                       RWDEBUG("No response to proxied request in 'max_request_time'.  Stopping it.");
-                       request->master_state = REQUEST_STOP_PROCESSING;
-                       request_done(request, FR_ACTION_DONE);
-                       break;
-               }
-
-               rad_assert(request->proxy != NULL);
-#ifdef WITH_COA
                /*
-                *      Ugh.
+                *      If there's a child thread processing it,
+                *      complain.
                 */
-               if (request->packet->code != request->proxy->code) {
-                       if (request->proxy_reply) {
-                               request->process = coa_running;
-                       } else {
-                               request->process = coa_wait_for_reply;
-                       }
-               } else
-#endif
-
-               if (request->proxy_reply) {
-                       request->process = proxy_running;
-               } else {
-                       request->process = proxy_wait_for_reply;
-               }
-
-               when = request->proxy->timestamp;
-               tv_add(&when, request->delay);
-
-               if (timercmp(&now, &when, >=)) {
-                       request->process(request, FR_ACTION_TIMER);
-                       return;
+               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>");
+                       exec_trigger(request, NULL, "server.thread.unresponsive", true);
                }
-
+#endif
                /*
-                *      Leave the initial delay alone.
+                *      Tell the request that it's done.
                 */
-               STATE_MACHINE_TIMER(FR_ACTION_TIMER);
-               return;
-#endif /* WITH_PROXY */
-
-       case REQUEST_RESPONSE_DELAY:
-               rad_assert(request->response_delay.tv_sec > 0);
-#ifdef WITH_COA
-               rad_assert(!request->proxy || (request->packet->code == request->proxy->code));
-#endif
-
-               request->process = request_response_delay;
-
-               when = request->reply->timestamp;
-
-               tv_add(&when, request->response_delay.tv_sec * USEC);
-               tv_add(&when, request->response_delay.tv_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");
-               request->listener->send(request->listener, request);
-               debug_packet(request, request->reply, false);
-               request->child_state = REQUEST_CLEANUP_DELAY;
-               /* FALL-THROUGH */
-
-       case REQUEST_CLEANUP_DELAY:
-               rad_assert(request->root->cleanup_delay > 0);
-
-#ifdef WITH_COA
-               rad_assert(!request->proxy || (request->packet->code == request->proxy->code));
-#endif
-
-               request->process = request_cleanup_delay;
-
-               when = request->reply->timestamp;
-               when.tv_sec += request->root->cleanup_delay;
-
-               if (timercmp(&when, &now, >)) {
-#ifdef DEBUG_STATE_MACHINE
-                       if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
-#endif
-                       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
-                       return;
-               } /* else it's time to clean up */
-               /* FALL-THROUGH */
-
-       case REQUEST_DONE:
                goto done;
        }
 
+       /*
+        *      Sleep for some more.  We HOPE that the child will
+        *      become responsive at some point in the future.  We do
+        *      this by adding 50% to the current timer.
+        */
+       when = now;
+       tv_add(&when, request->delay);
+       request->delay += request->delay >> 1;
+       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+       return false;
 }
 
 static void request_queue_or_run(REQUEST *request,
@@ -1054,7 +968,7 @@ static void request_queue_or_run(REQUEST *request,
         */
        if (request->master_state == REQUEST_STOP_PROCESSING) {
 #ifdef DEBUG_STATE_MACHINE
-               if (debug_flag) printf("(%u) ********\tSTATE %s M-%s causes C-%s-> C-%s\t********\n",
+               if (rad_debug_lvl) 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],
@@ -1110,82 +1024,53 @@ static void request_queue_or_run(REQUEST *request,
 #endif
 }
 
-STATE_MACHINE_DECL(request_common)
-{
-#ifdef WITH_PROXY
-       char buffer[128];
-#endif
-
-       VERIFY_REQUEST(request);
-
-       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
-               /*
-                *      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) 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:
-               /*
-                *      We're in the master thread, ask the child to
-                *      stop processing the request.
-                */
-               request_done(request, action);
-               return;
-
-       case FR_ACTION_TIMER:
-               request_process_timer(request);
-               return;
-
-#ifdef WITH_PROXY
-       case FR_ACTION_PROXY_REPLY:
-               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);
-               return;
-#endif
 
-       default:
-               RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
-               break;
-       }
+static void request_dup(REQUEST *request)
+{
+       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);
 }
 
-STATE_MACHINE_DECL(request_cleanup_delay)
+
+/** Sit on a request until it's time to clean it up.
+ *
+ *  A NAS may not see a response from the server.  When the NAS
+ *  retransmits, we want to be able to send a cached reply back.  The
+ *  alternative is to re-process the packet, which does bad things for
+ *  EAP, among others.
+ *
+ *  IF we do see a NAS retransmit, we extend the cleanup delay,
+ *  because the NAS might miss our cached reply.
+ *
+ *  Otherwise, once we reach cleanup_delay, we transition to DONE.
+ *
+ *  \dot
+ *     digraph cleanup_delay {
+ *             cleanup_delay;
+ *             send_reply [ label = "send_reply\nincrease cleanup delay" ];
+ *
+ *             cleanup_delay -> send_reply [ label = "DUP" ];
+ *             send_reply -> cleanup_delay;
+ *             cleanup_delay -> proxy_reply_too_late [ label = "PROXY_REPLY", arrowhead = "none" ];
+ *             cleanup_delay -> cleanup_delay [ label = "TIMER < timeout" ];
+ *             cleanup_delay -> done [ label = "TIMER >= timeout" ];
+ *     }
+ *  \enddot
+ */
+static void request_cleanup_delay(REQUEST *request, int action)
 {
-       struct timeval when;
+       struct timeval when, now;
 
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
        ASSERT_MASTER;
+       COA_SEPARATE;
+       CHECK_FOR_STOP;
 
        switch (action) {
        case FR_ACTION_DUP:
@@ -1199,22 +1084,36 @@ STATE_MACHINE_DECL(request_cleanup_delay)
                 *      Double the cleanup_delay to catch retransmits.
                 */
                when = request->reply->timestamp;
-               request->delay += request->delay ;
+               request->delay += request->delay;
                when.tv_sec += request->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:
+               proxy_reply_too_late(request);
+               break;
 #endif
+
        case FR_ACTION_TIMER:
-               request_common(request, action);
-               return;
+               fr_event_now(el, &now);
+
+               rad_assert(request->root->cleanup_delay > 0);
+
+               when = request->reply->timestamp;
+               when.tv_sec += request->root->cleanup_delay;
+
+               if (timercmp(&when, &now, >)) {
+#ifdef DEBUG_STATE_MACHINE
+                       if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
+#endif
+                       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+                       return;
+               } /* else it's time to clean up */
+
+               request_done(request, REQUEST_DONE);
+               break;
 
        default:
                RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
@@ -1222,27 +1121,79 @@ STATE_MACHINE_DECL(request_cleanup_delay)
        }
 }
 
-STATE_MACHINE_DECL(request_response_delay)
+
+/** Sit on a request until it's time to respond to it.
+ *
+ *  For security reasons, rejects (and maybe some other) packets are
+ *  delayed for a while before we respond.  This delay means that
+ *  badly behaved NASes don't hammer the server with authentication
+ *  attempts.
+ *
+ *  Otherwise, once we reach response_delay, we send the reply, and
+ *  transition to cleanup_delay.
+ *
+ *  \dot
+ *     digraph response_delay {
+ *             response_delay -> proxy_reply_too_late [ label = "PROXY_REPLY", arrowhead = "none" ];
+ *             response_delay -> response_delay [ label = "DUP, TIMER < timeout" ];
+ *             response_delay -> send_reply [ label = "TIMER >= timeout" ];
+ *             send_reply -> cleanup_delay;
+ *     }
+ *  \enddot
+ */
+static void request_response_delay(REQUEST *request, int action)
 {
+       struct timeval when, now;
+
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
        ASSERT_MASTER;
+       COA_SEPARATE;
+       CHECK_FOR_STOP;
 
        switch (action) {
        case FR_ACTION_DUP:
                ERROR("(%u) Discarding duplicate request from "
-                      "client %s port %d - ID: %u due to delayed response",
-                      request->number, request->client->shortname,
-                      request->packet->src_port,request->packet->id);
-               return;
+                     "client %s port %d - ID: %u due to delayed response",
+                     request->number, request->client->shortname,
+                     request->packet->src_port,request->packet->id);
+               break;
 
 #ifdef WITH_PROXY
        case FR_ACTION_PROXY_REPLY:
+               proxy_reply_too_late(request);
+               break;
 #endif
-       case FR_ACTION_CONFLICTING:
+
        case FR_ACTION_TIMER:
-               request_common(request, action);
+               fr_event_now(el, &now);
+
+               /*
+                *      See if it's time to send the reply.  If not,
+                *      we wait some more.
+                */
+               when = request->reply->timestamp;
+
+               tv_add(&when, request->response_delay.tv_sec * USEC);
+               tv_add(&when, request->response_delay.tv_usec);
+
+               if (timercmp(&when, &now, >)) {
+#ifdef DEBUG_STATE_MACHINE
+                       if (rad_debug_lvl) 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, false);
+               request->listener->send(request->listener, request);
+
+               /*
+                *      Clean up the request.
+                */
+               request_cleanup_delay_init(request);
                break;
 
        default:
@@ -1252,7 +1203,7 @@ STATE_MACHINE_DECL(request_response_delay)
 }
 
 
-static int CC_HINT(nonnull) request_pre_handler(REQUEST *request, UNUSED int action)
+static int request_pre_handler(REQUEST *request, UNUSED int action)
 {
        int rcode;
 
@@ -1268,8 +1219,8 @@ static int CC_HINT(nonnull) request_pre_handler(REQUEST *request, UNUSED int act
         *      process it.
         */
        if (request->packet->dst_port == 0) {
-               request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
-               request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+               request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
                return 1;
        }
 
@@ -1300,49 +1251,42 @@ static int CC_HINT(nonnull) request_pre_handler(REQUEST *request, UNUSED int act
        }
 
        if (!request->username) {
-               request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
        }
 
        return 1;
 }
 
-STATE_MACHINE_DECL(request_finish)
+
+/**  Do the final processing of a request before we reply to the NAS.
+ *
+ *  Various cleanups, suppress responses, copy Proxy-State, and set
+ *  response_delay or cleanup_delay;
+ */
+static void request_finish(REQUEST *request, int action)
 {
        VALUE_PAIR *vp;
 
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       CHECK_FOR_STOP;
 
        (void) action;  /* -Wunused */
 
-       if (request->master_state == REQUEST_STOP_PROCESSING) {
-#ifdef WITH_DETAIL
-               /*
-                *      Always send a reply to the detail listener.
-                */
-               if (request->listener->type == RAD_LISTEN_DETAIL) {
-                       goto do_detail;
-               }
-#endif
-               NO_CHILD_THREAD;
-               request->child_state = REQUEST_DONE;
-               return;
-       }
-
 #ifdef WITH_COA
        /*
         *      Don't do post-auth if we're a CoA request originated
         *      from an Access-Request.  See request_alloc_coa() for
         *      details.
         */
-       if (request->options == 1) goto done;
+       if ((request->options & RAD_REQUEST_OPTION_COA) != 0) goto done;
 #endif
 
        /*
         *      Override the response code if a control:Response-Packet-Type attribute is present.
         */
-       vp = pairfind(request->config_items, PW_RESPONSE_PACKET_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_RESPONSE_PACKET_TYPE, 0, TAG_ANY);
        if (vp) {
                if (vp->vp_integer == 256) {
                        RDEBUG2("Not responding to request");
@@ -1356,9 +1300,8 @@ STATE_MACHINE_DECL(request_finish)
         */
        else if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
                if (request->reply->code == 0) {
-                       vp = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
-
-                       if (!vp || (vp->vp_integer != PW_CODE_ACCESS_REJECT)) {
+                       vp = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
+                       if (!vp || (vp->vp_integer != 5)) {
                                RDEBUG2("There was no response configured: "
                                        "rejecting request");
                        }
@@ -1370,9 +1313,9 @@ STATE_MACHINE_DECL(request_finish)
        /*
         *      Copy Proxy-State from the request to the reply.
         */
-       vp = paircopy_by_num(request->reply, request->packet->vps,
+       vp = fr_pair_list_copy_by_num(request->reply, request->packet->vps,
                       PW_PROXY_STATE, 0, TAG_ANY);
-       if (vp) pairadd(&request->reply->vps, vp);
+       if (vp) fr_pair_add(&request->reply->vps, vp);
 
        /*
         *      Call Post-Auth for Access-Request packets.
@@ -1381,6 +1324,14 @@ STATE_MACHINE_DECL(request_finish)
                rad_postauth(request);
        }
 
+#ifdef WITH_COA
+       /*
+        *      Maybe originate a CoA request.
+        */
+       if ((action == FR_ACTION_RUN) && !request->proxy && request->coa) {
+               request_coa_originate(request);
+       }
+#endif
 
        /*
         *      Clean up.  These are no longer needed.
@@ -1394,8 +1345,7 @@ STATE_MACHINE_DECL(request_finish)
         */
        if (request->packet->dst_port == 0) {
                RDEBUG("Finished internally proxied request.");
-               NO_CHILD_THREAD;
-               request->child_state = REQUEST_DONE;
+               FINAL_STATE(REQUEST_DONE);
                return;
        }
 
@@ -1404,15 +1354,16 @@ STATE_MACHINE_DECL(request_finish)
         *      Always send the reply to the detail listener.
         */
        if (request->listener->type == RAD_LISTEN_DETAIL) {
-       do_detail:
                request->simul_max = 1;
-               request->listener->send(request->listener, request);
+
                /*
                 *      But only print the reply if there is one.
                 */
                if (request->reply->code != 0) {
                        debug_packet(request, request->reply, false);
                }
+
+               request->listener->send(request->listener, request);
                goto done;
        }
 #endif
@@ -1457,6 +1408,27 @@ STATE_MACHINE_DECL(request_finish)
            (request->root->reject_delay.tv_sec > 0)) {
                request->response_delay = request->root->reject_delay;
 
+               vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY);
+               if (vp) {
+                       if (vp->vp_integer <= 10) {
+                               request->response_delay.tv_sec = vp->vp_integer;
+                       } else {
+                               request->response_delay.tv_sec = 10;
+                       }
+                       request->response_delay.tv_usec = 0;
+               } else {
+                       vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY_USEC, 0, TAG_ANY);
+                       if (vp) {
+                               if (vp->vp_integer <= 10 * USEC) {
+                                       request->response_delay.tv_sec = vp->vp_integer / USEC;
+                                       request->response_delay.tv_usec = vp->vp_integer % USEC;
+                               } else {
+                                       request->response_delay.tv_sec = 10;
+                                       request->response_delay.tv_usec = 0;
+                               }
+                       }
+               }
+
 #ifdef WITH_PROXY
                /*
                 *      If we timed out a proxy packet, don't delay
@@ -1472,98 +1444,82 @@ STATE_MACHINE_DECL(request_finish)
        /*
         *      Send the reply.
         */
-       if (request->response_delay.tv_sec == 0) {
-               rad_assert(request->response_delay.tv_usec == 0);
+       if ((request->response_delay.tv_sec == 0) &&
+           (request->response_delay.tv_usec == 0)) {
 
                /*
                 *      Don't print a reply if there's none to send.
                 */
                if (request->reply->code != 0) {
-                       request->listener->send(request->listener, request);
+                       if (rad_debug_lvl && request->state &&
+                           (request->reply->code == PW_CODE_ACCESS_ACCEPT)) {
+                               if (!fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY)) {
+                                       RWDEBUG2("Unused attributes found in &session-state:");
+                               }
+                       }
+
                        debug_packet(request, request->reply, false);
+                       request->listener->send(request->listener, request);
                }
+
        done:
                RDEBUG2("Finished request");
-               request->component = "<core>";
-               request->module = "<done>";
-
-#ifdef WITH_ACCOUNTING
-               if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
-                       NO_CHILD_THREAD;
-                       request->child_state = REQUEST_DONE;
-               } else
-#endif
+               request_cleanup_delay_init(request);
 
-#ifdef WITH_COA
+       } else {
                /*
-                *      If we've originated this CoA request, it gets
-                *      cleaned up now.
+                *      Encode and sign it here, so that the master
+                *      thread can just send the encoded data, which
+                *      means it does less work.
                 */
-               if (request->proxy &&
-                   ((request->proxy->code == PW_CODE_COA_REQUEST) ||
-                    (request->proxy->code == PW_CODE_DISCONNECT_REQUEST)) &&
-                   (request->packet->code != request->proxy->code)) {
-                       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.%06d seconds",
                        (int) request->response_delay.tv_sec, (int) request->response_delay.tv_usec);
-               NO_CHILD_THREAD;
-               request->child_state = REQUEST_RESPONSE_DELAY;
+               request->listener->encode(request->listener, request);
+               request->process = request_response_delay;
+
+               FINAL_STATE(REQUEST_RESPONSE_DELAY);
        }
 }
 
-STATE_MACHINE_DECL(request_running)
+/** Process a request from a client.
+ *
+ *  The outcome might be that the request is proxied.
+ *
+ *  \dot
+ *     digraph running {
+ *             running -> running [ label = "TIMER < max_request_time" ];
+ *             running -> done [ label = "TIMER >= max_request_time" ];
+ *             running -> proxy [ label = "proxied" ];
+ *             running -> dup [ label = "DUP", arrowhead = "none" ];
+ *     }
+ *  \enddot
+ */
+static void request_running(REQUEST *request, int action)
 {
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       CHECK_FOR_STOP;
 
        switch (action) {
        case FR_ACTION_TIMER:
-               request_process_timer(request);
+               COA_SEPARATE;
+               (void) request_max_time(request);
                break;
 
-       case FR_ACTION_CONFLICTING:
        case FR_ACTION_DUP:
-               request_common(request, action);
-               return;
-
-#ifdef WITH_PROXY
-               /*
-                *      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.
-                */
-       case FR_ACTION_PROXY_REPLY:
-               request->child_state = REQUEST_RUNNING;
-               request->process = proxy_running;
-               request->process(request, FR_ACTION_RUN);
+               request_dup(request);
                break;
-#endif
 
        case FR_ACTION_RUN:
                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",
+                       if (rad_debug_lvl) 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;
+                       FINAL_STATE(REQUEST_DONE);
                        break;
                }
 
@@ -1577,7 +1533,7 @@ STATE_MACHINE_DECL(request_running)
                if ((action == FR_ACTION_RUN) &&
                    request_will_proxy(request)) {
 #ifdef DEBUG_STATE_MACHINE
-                       if (debug_flag) printf("(%u) ********\tWill Proxy\t********\n", request->number);
+                       if (rad_debug_lvl) printf("(%u) ********\tWill Proxy\t********\n", request->number);
 #endif
                        /*
                         *      If this fails, it
@@ -1590,16 +1546,7 @@ STATE_MACHINE_DECL(request_running)
 #endif
                {
 #ifdef DEBUG_STATE_MACHINE
-                       if (debug_flag) printf("(%u) ********\tFinished\t********\n", request->number);
-#endif
-
-#ifdef WITH_COA
-                       /*
-                        *      Maybe originate a CoA request.
-                        */
-                       if ((action == FR_ACTION_RUN) && request->coa) {
-                               request_coa_originate(request);
-                       }
+                       if (rad_debug_lvl) printf("(%u) ********\tFinished\t********\n", request->number);
 #endif
 
 #ifdef WITH_PROXY
@@ -1639,6 +1586,10 @@ int request_receive(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PACKET *pack
        {
                sock = listener->data;
                sock->last_packet = now.tv_sec;
+
+#ifdef WITH_TCP
+               packet->proto = sock->proto;
+#endif
        }
 
        /*
@@ -1648,8 +1599,11 @@ int request_receive(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PACKET *pack
 
        packet_p = rbtree_finddata(pl, &packet);
        if (packet_p) {
+               rad_child_state_t child_state;
+
                request = fr_packet2myptr(REQUEST, packet, packet_p);
                rad_assert(request->in_request_hash);
+               child_state = request->child_state;
 
                /*
                 *      Same src/dst ip/port, length, and
@@ -1659,66 +1613,71 @@ int request_receive(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PACKET *pack
                    (memcmp(request->packet->vector, packet->vector,
                            sizeof(packet->vector)) == 0)) {
 
-                       /*
-                        *      If the request is running, don't muck
-                        *      with it.
-                        */
-                       if ((request->child_state != REQUEST_DONE) &&
-                           (request->child_state != REQUEST_RESPONSE_DELAY) &&
-                           (request->child_state != REQUEST_CLEANUP_DELAY)) {
-                               request->process(request, FR_ACTION_DUP);
-
 #ifdef WITH_STATS
-                               switch (packet->code) {
-                               case PW_CODE_ACCESS_REQUEST:
-                                       FR_STATS_INC(auth, total_dup_requests);
-                                       break;
+                       switch (packet->code) {
+                       case PW_CODE_ACCESS_REQUEST:
+                               FR_STATS_INC(auth, total_dup_requests);
+                               break;
 
 #ifdef WITH_ACCOUNTING
-                               case PW_CODE_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_CODE_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_CODE_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;
-                               }
-#endif /* WITH_STATS */
-                               return 0; /* duplicate of live request */
+                       default:
+                               break;
                        }
-#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
+#endif /* WITH_STATS */
 
                        /*
-                        *      Clean up the old request, and allow
-                        *      the new one to continue.
+                        *      Tell the state machine that there's a
+                        *      duplicate request.
                         */
-                       request_done(request, FR_ACTION_DONE);
-                       request = NULL;
+                       request->process(request, FR_ACTION_DUP);
+                       return 0; /* duplicate of live request */
+               }
 
-               } else {
+               /*
+                *      Mark the request as done ASAP, and before we
+                *      log anything.  The child may stop processing
+                *      the request just as we're logging the
+                *      complaint.
+                */
+               request_done(request, FR_ACTION_DONE);
+               request = NULL;
+
+               /*
+                *      It's a new request, not a duplicate.  If the
+                *      old one is done, then we can clean it up.
+                */
+               if (child_state <= REQUEST_RUNNING) {
                        /*
-                        *      Say we're ignoring the old one, and continue
-                        *      to process the new one.
+                        *      The request is still QUEUED or RUNNING.  That's a problem.
                         */
-                       request->process(request, FR_ACTION_CONFLICTING);
-                       request = NULL;
+                       ERROR("Received conflicting packet from "
+                             "client %s port %d - ID: %u due to "
+                             "unfinished request.  Giving up on old request.",
+                             client->shortname,
+                             packet->src_port, packet->id);
                }
-       }
+
+               /*
+                *      Mark the old request as done.  If there's no
+                *      child, the request will be cleaned up
+                *      immediately.  If there is a child, we'll set a
+                *      timer to go clean up the request.
+                */
+       } /* else the new packet is unique */
 
        /*
         *      Quench maximum number of outstanding requests.
@@ -1750,8 +1709,31 @@ skip_dup:
                sock->rate_pps_now++;
        }
 
-       request = request_setup(ctx, listener, packet, client, fun);
-       if (!request) return 1;
+       /*
+        *      Allocate a pool for the request.
+        */
+       if (!ctx) {
+               ctx = talloc_pool(NULL, main_config.talloc_pool_size);
+               if (!ctx) return 0;
+               talloc_set_name_const(ctx, "request_receive_pool");
+
+               /*
+                *      The packet is still allocated from a different
+                *      context, but oh well.
+                */
+               (void) talloc_steal(ctx, packet);
+       }
+
+       request = request_setup(ctx, listener, packet, client, fun);
+       if (!request) {
+               talloc_free(ctx);
+               return 1;
+       }
+
+       /*
+        *      Mark it as a "real" request with a context.
+        */
+       request->options |= RAD_REQUEST_OPTION_CTX;
 
        /*
         *      Remember the request in the list.
@@ -1775,8 +1757,8 @@ skip_dup:
 #endif
 
                request->listener->decode(request->listener, 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);
+               request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
 
                fun(request);
 
@@ -1785,6 +1767,10 @@ skip_dup:
                } else {
                        RDEBUG("Not sending reply");
                }
+
+               /*
+                *      Don't do delayed reject.  Oh well.
+                */
                request_free(request);
                return 1;
        }
@@ -1825,13 +1811,13 @@ static REQUEST *request_setup(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PA
        request->number = request_num_counter++;
        request->priority = listener->type;
        request->master_state = REQUEST_ACTIVE;
+       request->child_state = REQUEST_RUNNING;
 #ifdef DEBUG_STATE_MACHINE
-       if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
+       if (rad_debug_lvl) 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_RUNNING;
        request->handle = fun;
        NO_CHILD_THREAD;
 
@@ -1916,10 +1902,10 @@ static void tcp_socket_timer(void *ctx)
 
        ASSERT_MASTER;
 
-       fr_event_now(el, &now);
-
        if (listener->status != RAD_LISTEN_STATUS_KNOWN) return;
 
+       fr_event_now(el, &now);
+
        switch (listener->type) {
 #ifdef WITH_PROXY
        case RAD_LISTEN_PROXY:
@@ -1951,7 +1937,28 @@ static void tcp_socket_timer(void *ctx)
 
                do_close:
 
-                       listener->status = RAD_LISTEN_STATUS_EOL;
+#ifdef WITH_PROXY
+                       /*
+                        *      Proxy sockets get frozen, so that we don't use
+                        *      them for new requests.  But we do keep them
+                        *      open to listen for replies to requests we had
+                        *      previously sent.
+                        */
+                       if (listener->type == RAD_LISTEN_PROXY) {
+                               PTHREAD_MUTEX_LOCK(&proxy_mutex);
+                               if (!fr_packet_list_socket_freeze(proxy_list,
+                                                                 listener->fd)) {
+                                       ERROR("Fatal error freezing socket: %s", fr_strerror());
+                                       fr_exit(1);
+                               }
+                               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+                       }
+#endif
+
+                       /*
+                        *      Mark the socket as "don't use if at all possible".
+                        */
+                       listener->status = RAD_LISTEN_STATUS_FROZEN;
                        event_new_fd(listener);
                        return;
                }
@@ -1990,6 +1997,7 @@ static void tcp_socket_timer(void *ctx)
         */
        end.tv_usec = USEC / 2;
 
+       ASSERT_MASTER;
        if (!fr_event_insert(el, tcp_socket_timer, listener, &end, &sock->ev)) {
                rad_panic("Failed to insert event");
        }
@@ -2059,6 +2067,7 @@ static int eol_listener(void *ctx, void *data)
        if (request->listener != this) return 0;
 
        request->master_state = REQUEST_STOP_PROCESSING;
+       request->process = request_done;
 
        return 0;
 }
@@ -2094,14 +2103,32 @@ static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
                request->home_server->currently_outstanding--;
 
                /*
-                *      If we're NOT sending it packets, then we don't know
-                *      if it's alive or dead.
+                *      If we're NOT sending it packets, AND it's been
+                *      a while since we got a response, then we don't
+                *      know if it's alive or dead.
                 */
                if ((request->home_server->currently_outstanding == 0) &&
                    (request->home_server->state == HOME_STATE_ALIVE)) {
-                       request->home_server->state = HOME_STATE_UNKNOWN;
-                       request->home_server->last_packet_sent = 0;
-                       request->home_server->last_packet_recv = 0;
+                       struct timeval when, now;
+
+                       when.tv_sec = request->home_server->last_packet_recv ;
+                       when.tv_usec = 0;
+
+                       timeradd(&when, request_response_window(request), &when);
+                       gettimeofday(&now, NULL);
+
+                       /*
+                        *      last_packet + response_window
+                        *
+                        *      We *administratively* mark the home
+                        *      server as "unknown" state, because we
+                        *      haven't seen a packet for a while.
+                        */
+                       if (timercmp(&now, &when, >)) {
+                               request->home_server->state = HOME_STATE_UNKNOWN;
+                               request->home_server->last_packet_sent = 0;
+                               request->home_server->last_packet_recv = 0;
+                       }
                }
        }
 
@@ -2116,7 +2143,6 @@ static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
         *      the mutex.  This guarantees that when another thread
         *      grabs the mutex, the "not in hash" flag is correct.
         */
-       RDEBUG3("proxy: request is no longer in proxy hash");
 }
 
 static void remove_from_proxy_hash(REQUEST *request)
@@ -2149,7 +2175,8 @@ static void remove_from_proxy_hash(REQUEST *request)
 static int insert_into_proxy_hash(REQUEST *request)
 {
        char buf[128];
-       int rcode = 0, tries;
+       int tries;
+       bool success = false;
        void *proxy_listener;
 
        VERIFY_REQUEST(request);
@@ -2169,13 +2196,11 @@ static int insert_into_proxy_hash(REQUEST *request)
                listen_socket_t *sock;
 
                RDEBUG3("proxy: Trying to allocate ID (%d/2)", tries);
-               rcode = fr_packet_list_id_alloc(proxy_list,
+               success = fr_packet_list_id_alloc(proxy_list,
                                                request->home_server->proto,
                                                &request->proxy, &proxy_listener);
-               if ((debug_flag > 2) && (rcode == 0)) {
-                       RDEBUG("proxy: Failed allocating ID: %s", fr_strerror());
-               }
-               if (rcode > 0) break;
+               if (success) break;
+
                if (tries > 0) continue; /* try opening new socket only once */
 
 #ifdef HAVE_PTHREAD_H
@@ -2183,7 +2208,7 @@ static int insert_into_proxy_hash(REQUEST *request)
 #endif
 
                RDEBUG3("proxy: Trying to open a new listener to the home server");
-               this = proxy_new_listener(request->home_server, 0);
+               this = proxy_new_listener(proxy_ctx, request->home_server, 0);
                if (!this) {
                        PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
                        goto fail;
@@ -2223,7 +2248,7 @@ static int insert_into_proxy_hash(REQUEST *request)
                PTHREAD_MUTEX_LOCK(&proxy_mutex);
        }
 
-       if (!proxy_listener || (rcode == 0)) {
+       if (!proxy_listener || !success) {
                PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
                REDEBUG2("proxy: Failed allocating Id for proxied request");
        fail:
@@ -2276,36 +2301,57 @@ static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply)
        /*
         *      Delete any reply we had accumulated until now.
         */
-       pairfree(&request->reply->vps);
+       RDEBUG2("Clearing existing &reply: attributes");
+       fr_pair_list_free(&request->reply->vps);
 
        /*
         *      Run the packet through the post-proxy stage,
         *      BEFORE playing games with the attributes.
         */
-       vp = pairfind(request->config_items, PW_POST_PROXY_TYPE, 0, TAG_ANY);
-
+       vp = fr_pair_find_by_num(request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
+       if (vp) {
+               post_proxy_type = vp->vp_integer;
        /*
-        *      If we have a proxy_reply, and it was a reject, setup
-        *      post-proxy-type Reject
+        *      If we have a proxy_reply, and it was a reject, or a NAK
+        *      setup Post-Proxy <type>.
+        *
+        *      If the <type> doesn't have a section, then the Post-Proxy
+        *      section is ignored.
         */
-       if (!vp && reply &&
-           reply->code == PW_CODE_ACCESS_REJECT) {
-               DICT_VALUE      *dval;
+       } else if (reply) {
+               DICT_VALUE *dval = NULL;
 
-               dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Reject");
-               if (dval) {
-                       vp = radius_paircreate(request, &request->config_items,
-                                              PW_POST_PROXY_TYPE, 0);
+               switch (reply->code) {
+               case PW_CODE_ACCESS_REJECT:
+                       dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Reject");
+                       if (dval) post_proxy_type = dval->value;
+                       break;
+
+               case PW_CODE_DISCONNECT_NAK:
+                       dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, fr_packet_codes[reply->code]);
+                       if (dval) post_proxy_type = dval->value;
+                       break;
+
+               case PW_CODE_COA_NAK:
+                       dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, fr_packet_codes[reply->code]);
+                       if (dval) post_proxy_type = dval->value;
+                       break;
+
+               default:
+                       break;
+               }
 
+               /*
+                *      Create config:Post-Proxy-Type
+                */
+               if (dval) {
+                       vp = radius_pair_create(request, &request->config, PW_POST_PROXY_TYPE, 0);
                        vp->vp_integer = dval->value;
                }
        }
 
-       if (vp) {
-               post_proxy_type = vp->vp_integer;
-
-               RDEBUG2("Found Post-Proxy-Type %s", dict_valnamebyattr(PW_POST_PROXY_TYPE, 0, post_proxy_type));
-       }
+       if (post_proxy_type > 0) RDEBUG2("Found Post-Proxy-Type %s",
+                                        dict_valnamebyattr(PW_POST_PROXY_TYPE, 0, post_proxy_type));
 
        if (reply) {
                VERIFY_PACKET(reply);
@@ -2362,14 +2408,14 @@ static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply)
         *      running Post-Proxy-Type = Fail.
         */
        if (reply) {
-               pairadd(&request->reply->vps, paircopy(request->reply, reply->vps));
+               fr_pair_add(&request->reply->vps, fr_pair_list_copy(request->reply, reply->vps));
 
                /*
                 *      Delete the Proxy-State Attributes from
                 *      the reply.  These include Proxy-State
                 *      attributes from us and remote server.
                 */
-               pairdelete(&request->reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+               fr_pair_delete_by_num(&request->reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
        }
 
        switch (rcode) {
@@ -2385,6 +2431,28 @@ static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply)
        return 1;
 }
 
+static void mark_home_server_alive(REQUEST *request, home_server_t *home)
+{
+       char buffer[128];
+
+       home->state = HOME_STATE_ALIVE;
+       home->response_timeouts = 0;
+       exec_trigger(request, home->cs, "home_server.alive", false);
+       home->currently_outstanding = 0;
+       home->num_sent_pings = 0;
+       home->num_received_pings = 0;
+       gettimeofday(&home->revive_time, NULL);
+
+       fr_event_delete(el, &home->ev);
+
+       RPROXY("Marking home server %s port %d alive",
+              inet_ntop(request->proxy->dst_ipaddr.af,
+                        &request->proxy->dst_ipaddr.ipaddr,
+                        buffer, sizeof(buffer)),
+              request->proxy->dst_port);
+}
+
+
 int request_proxy_reply(RADIUS_PACKET *packet)
 {
        RADIUS_PACKET **proxy_p;
@@ -2399,7 +2467,8 @@ int request_proxy_reply(RADIUS_PACKET *packet)
 
        if (!proxy_p) {
                PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
-               PROXY( "No outstanding request was found for reply from host %s port %d - ID %u",
+               PROXY("No outstanding request was found for %s packet from host %s port %d - ID %u",
+                      fr_packet_codes[packet->code],
                       inet_ntop(packet->src_ipaddr.af,
                                 &packet->src_ipaddr.ipaddr,
                                 buffer, sizeof(buffer)),
@@ -2470,28 +2539,73 @@ int request_proxy_reply(RADIUS_PACKET *packet)
        packet->timestamp = now;
        request->priority = RAD_LISTEN_PROXY;
 
+#ifdef WITH_STATS
        /*
-        *      We've received a reply.  If we hadn't been sending it
-        *      packets for a while, just mark it alive.
+        *      Update the proxy listener stats here, because only one
+        *      thread accesses that at a time.  The home_server and
+        *      main proxy_*_stats structures are updated once the
+        *      request is cleaned up.
         */
-       if (request->home_server->state == HOME_STATE_UNKNOWN) {
-               request->home_server->state = HOME_STATE_ALIVE;
-               request->home_server->response_timeouts = 0;
-       }
+       request->proxy_listener->stats.total_responses++;
 
-#ifdef WITH_STATS
        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_CODE_ACCESS_REQUEST) {
+       switch (request->proxy->code) {
+       case PW_CODE_ACCESS_REQUEST:
                proxy_auth_stats.last_packet = packet->timestamp.tv_sec;
+
+               if (request->proxy_reply->code == PW_CODE_ACCESS_ACCEPT) {
+                       request->proxy_listener->stats.total_access_accepts++;
+
+               } else if (request->proxy_reply->code == PW_CODE_ACCESS_REJECT) {
+                       request->proxy_listener->stats.total_access_rejects++;
+
+               } else if (request->proxy_reply->code == PW_CODE_ACCESS_CHALLENGE) {
+                       request->proxy_listener->stats.total_access_challenges++;
+               }
+               break;
+
 #ifdef WITH_ACCOUNTING
-       } else if (request->proxy->code == PW_CODE_ACCOUNTING_REQUEST) {
+       case PW_CODE_ACCOUNTING_REQUEST:
+               request->proxy_listener->stats.total_responses++;
                proxy_acct_stats.last_packet = packet->timestamp.tv_sec;
+               break;
+
+#endif
+
+#ifdef WITH_COA
+       case PW_CODE_COA_REQUEST:
+               request->proxy_listener->stats.total_responses++;
+               proxy_coa_stats.last_packet = packet->timestamp.tv_sec;
+               break;
+
+       case PW_CODE_DISCONNECT_REQUEST:
+               request->proxy_listener->stats.total_responses++;
+               proxy_dsc_stats.last_packet = packet->timestamp.tv_sec;
+               break;
+
+#endif
+       default:
+               break;
+       }
 #endif
+
+       /*
+        *      If we hadn't been sending the home server packets for
+        *      a while, just mark it alive.  Or, if it was zombie,
+        *      it's now responded, and is therefore alive.
+        */
+       if ((request->home_server->state == HOME_STATE_UNKNOWN) ||
+           (request->home_server->state == HOME_STATE_ZOMBIE)) {
+               mark_home_server_alive(request, request->home_server);
        }
-#endif /* WITH_STATS */
 
+       /*
+        *      Tell the request state machine that we have a proxy
+        *      reply.  Depending on the function, this should either
+        *      ignore it, or process it.
+        */
        request->process(request, FR_ACTION_PROXY_REPLY);
 
        return 1;
@@ -2508,10 +2622,12 @@ static int setup_post_proxy_fail(REQUEST *request)
        if (request->proxy->code == PW_CODE_ACCESS_REQUEST) {
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0,
                                      "Fail-Authentication");
-
+#ifdef WITH_ACCOUNTING
        } else if (request->proxy->code == PW_CODE_ACCOUNTING_REQUEST) {
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0,
                                      "Fail-Accounting");
+#endif
+
 #ifdef WITH_COA
        } else if (request->proxy->code == PW_CODE_COA_REQUEST) {
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-CoA");
@@ -2527,37 +2643,60 @@ static int setup_post_proxy_fail(REQUEST *request)
        if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail");
 
        if (!dval) {
-               pairdelete(&request->config_items, PW_POST_PROXY_TYPE, 0, TAG_ANY);
+               fr_pair_delete_by_num(&request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
                return 0;
        }
 
-       vp = pairfind(request->config_items, PW_POST_PROXY_TYPE, 0, TAG_ANY);
-       if (!vp) vp = radius_paircreate(request, &request->config_items,
+       vp = fr_pair_find_by_num(request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
+       if (!vp) vp = radius_pair_create(request, &request->config,
                                        PW_POST_PROXY_TYPE, 0);
        vp->vp_integer = dval->value;
 
        return 1;
 }
 
-STATE_MACHINE_DECL(proxy_no_reply)
+
+/** Process a request after the proxy has timed out.
+ *
+ *  Run the packet through Post-Proxy-Type Fail
+ *
+ *  \dot
+ *     digraph proxy_no_reply {
+ *             proxy_no_reply;
+ *
+ *             proxy_no_reply -> dup [ label = "DUP", arrowhead = "none" ];
+ *             proxy_no_reply -> timer [ label = "TIMER < max_request_time" ];
+ *             proxy_no_reply -> proxy_reply_too_late [ label = "PROXY_REPLY" arrowhead = "none"];
+ *             proxy_no_reply -> process_proxy_reply [ label = "RUN" ];
+ *             proxy_no_reply -> done [ label = "TIMER >= timeout" ];
+ *     }
+ *  \enddot
+ */
+static void proxy_no_reply(REQUEST *request, int action)
 {
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       CHECK_FOR_STOP;
 
        switch (action) {
-       case FR_ACTION_CONFLICTING:
        case FR_ACTION_DUP:
+               request_dup(request);
+               break;
+
        case FR_ACTION_TIMER:
+               (void) request_max_time(request);
+               break;
+
        case FR_ACTION_PROXY_REPLY:
-               request_common(request, action);
+               proxy_reply_too_late(request);
                break;
 
        case FR_ACTION_RUN:
                if (process_proxy_reply(request, NULL)) {
-                       request_finish(request, action);
+                       request->handle(request);
                }
-               request_done(request, FR_ACTION_DONE);
+               request_finish(request, action);
                break;
 
        default:
@@ -2566,35 +2705,79 @@ STATE_MACHINE_DECL(proxy_no_reply)
        }
 }
 
-STATE_MACHINE_DECL(proxy_running)
+/** Process the request after receiving a proxy reply.
+ *
+ *  Throught the post-proxy section, and the through the handler
+ *  function.
+ *
+ *  \dot
+ *     digraph proxy_running {
+ *             proxy_running;
+ *
+ *             proxy_running -> dup [ label = "DUP", arrowhead = "none" ];
+ *             proxy_running -> timer [ label = "TIMER < max_request_time" ];
+ *             proxy_running -> process_proxy_reply [ label = "RUN" ];
+ *             proxy_running -> done [ label = "TIMER >= timeout" ];
+ *     }
+ *  \enddot
+ */
+static void proxy_running(REQUEST *request, int action)
 {
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       CHECK_FOR_STOP;
 
        switch (action) {
-       case FR_ACTION_CONFLICTING:
        case FR_ACTION_DUP:
+               request_dup(request);
+               break;
+
        case FR_ACTION_TIMER:
-       case FR_ACTION_PROXY_REPLY:
-               request_common(request, action);
+               (void) request_max_time(request);
                break;
 
        case FR_ACTION_RUN:
                if (process_proxy_reply(request, request->proxy_reply)) {
                        request->handle(request);
-                       request_finish(request, action);
-               } else {
-                       request_done(request, FR_ACTION_DONE);
                }
+               request_finish(request, action);
                break;
 
-       default:
+       default:                /* duplicate proxy replies are suppressed */
                RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
                break;
        }
 }
 
+/** Determine if a #REQUEST needs to be proxied, and perform pre-proxy operations
+ *
+ * Whether a request will be proxied is determined by the attributes present
+ * in request->config. If any of the following attributes are found, the
+ * request may be proxied.
+ *
+ * The key attributes are:
+ *   - PW_PROXY_TO_REALM          - Specifies a realm the request should be proxied to.
+ *   - PW_HOME_SERVER_POOL        - Specifies a specific home server pool to proxy to.
+ *   - PW_PACKET_DST_IP_ADDRESS   - Specifies a specific IPv4 home server to proxy to.
+ *   - PW_PACKET_DST_IPV6_ADDRESS - Specifies a specific IPv6 home server to proxy to.
+ *
+ * Certain packet types such as #PW_CODE_STATUS_SERVER will never be proxied.
+ *
+ * If request should be proxied, will:
+ *   - Add request:Proxy-State
+ *   - Strip the current username value of its realm (depending on config)
+ *   - Create a CHAP-Challenge from the original request vector, if one doesn't already
+ *     exist.
+ *   - Call the pre-process section in the current server, or in the virtual server
+ *     associated with the home server pool we're proxying to.
+ *
+ * @todo A lot of this logic is RADIUS specific, and should be moved out into a protocol
+ *     specific function.
+ *
+ * @param request The #REQUEST to evaluate for proxying.
+ * @return 0 if not proxying, 1 if request should be proxied, -1 on error.
+ */
 static int request_will_proxy(REQUEST *request)
 {
        int rcode, pre_proxy_type = 0;
@@ -2616,7 +2799,7 @@ static int request_will_proxy(REQUEST *request)
         */
        if (request->reply->code != 0) return 0;
 
-       vp = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
        if (vp) {
                realm = realm_find2(vp->vp_strvalue);
                if (!realm) {
@@ -2648,7 +2831,7 @@ static int request_will_proxy(REQUEST *request)
                        return 0;
                }
 
-       } else if ((vp = pairfind(request->config_items, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
+       } else if ((vp = fr_pair_find_by_num(request->config, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
                int pool_type;
 
                switch (request->packet->code) {
@@ -2678,9 +2861,8 @@ static int request_will_proxy(REQUEST *request)
                /*
                 *      Send it directly to a home server (i.e. NAS)
                 */
-       } else if (((vp = pairfind(request->config_items, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY)) != NULL) ||
-                  ((vp = pairfind(request->config_items, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL)) {
-               VALUE_PAIR *port;
+       } else if (((vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY)) != NULL) ||
+                  ((vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL)) {
                uint16_t dst_port;
                fr_ipaddr_t dst_ipaddr;
 
@@ -2696,9 +2878,25 @@ static int request_will_proxy(REQUEST *request)
                        dst_ipaddr.prefix = 128;
                }
 
-               port = pairfind(request->config_items, PW_PACKET_DST_PORT, 0, TAG_ANY);
-               if (!port) {
-               dst_port = PW_COA_UDP_PORT;
+               vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_PORT, 0, TAG_ANY);
+               if (!vp) {
+                       if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
+                               dst_port = PW_AUTH_UDP_PORT;
+
+#ifdef WITH_ACCOUNTING
+                       } else if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
+                               dst_port = PW_ACCT_UDP_PORT;
+#endif
+
+#ifdef WITH_COA
+                       } else if ((request->packet->code == PW_CODE_COA_REQUEST) ||
+                                  (request->packet->code == PW_CODE_DISCONNECT_REQUEST)) {
+                               dst_port = PW_COA_UDP_PORT;
+#endif
+                       } else { /* shouldn't happen for RADIUS... */
+                               return 0;
+                       }
+
                } else {
                        dst_port = vp->vp_integer;
                }
@@ -2710,13 +2908,25 @@ static int request_will_proxy(REQUEST *request)
                if (!home) {
                        char buffer[256];
 
-                       WARN("No such CoA home server %s port %u",
+                       WARN("No such home server %s port %u",
                             inet_ntop(dst_ipaddr.af, &dst_ipaddr.ipaddr, buffer, sizeof(buffer)),
                             (unsigned int) dst_port);
                        return 0;
                }
 
-               goto do_home;
+               /*
+                *      The home server is alive (or may be alive).
+                *      Send the packet to the IP.
+                */
+               if (home->state != HOME_STATE_IS_DEAD) goto do_home;
+
+               /*
+                *      The home server is dead.  If you wanted
+                *      fail-over, you should have proxied to a pool.
+                *      Sucks to be you.
+                */
+
+               return 0;
 
        } else {
                return 0;
@@ -2755,7 +2965,7 @@ do_home:
        /*
         *      Remember that we sent the request to a Realm.
         */
-       if (realmname) pairmake_packet("Realm", realmname, T_OP_EQ);
+       if (realmname) pair_make_request("Realm", realmname, T_OP_EQ);
 
        /*
         *      Strip the name, if told to.
@@ -2764,7 +2974,7 @@ do_home:
         *      requests.
         */
        if (realm && (realm->strip_realm == true) &&
-          (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY)) != NULL) {
+          (strippedname = fr_pair_find_by_num(request->proxy->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY)) != NULL) {
                /*
                 *      If there's a Stripped-User-Name attribute in
                 *      the request, then use THAT as the User-Name
@@ -2778,10 +2988,10 @@ do_home:
                 *      from the vps list, and making the new
                 *      User-Name the head of the vps list.
                 */
-               vp = pairfind(request->proxy->vps, PW_USER_NAME, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->proxy->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!vp) {
                        vp_cursor_t cursor;
-                       vp = radius_paircreate(NULL, NULL,
+                       vp = radius_pair_create(NULL, NULL,
                                               PW_USER_NAME, 0);
                        rad_assert(vp != NULL); /* handled by above function */
                        /* Insert at the START of the list */
@@ -2790,7 +3000,7 @@ do_home:
                        fr_cursor_merge(&cursor, request->proxy->vps);
                        request->proxy->vps = vp;
                }
-               pairstrcpy(vp, strippedname->vp_strvalue);
+               fr_pair_value_strcpy(vp, strippedname->vp_strvalue);
 
                /*
                 *      Do NOT delete Stripped-User-Name.
@@ -2804,18 +3014,18 @@ do_home:
         *      anymore - we changed it.
         */
        if ((request->packet->code == PW_CODE_ACCESS_REQUEST) &&
-           pairfind(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
-           pairfind(request->proxy->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
-               vp = radius_paircreate(request->proxy, &request->proxy->vps, PW_CHAP_CHALLENGE, 0);
-               pairmemcpy(vp, request->packet->vector, sizeof(request->packet->vector));
+           fr_pair_find_by_num(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
+           fr_pair_find_by_num(request->proxy->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
+               vp = radius_pair_create(request->proxy, &request->proxy->vps, PW_CHAP_CHALLENGE, 0);
+               fr_pair_value_memcpy(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->proxy, &request->proxy->vps, PW_PROXY_STATE, 0);
-       pairsprintf(vp, "%u", request->packet->id);
+       vp = radius_pair_create(request->proxy, &request->proxy->vps, PW_PROXY_STATE, 0);
+       fr_pair_value_sprintf(vp, "%u", request->packet->id);
 
        /*
         *      Should be done BEFORE inserting into proxy hash, as
@@ -2826,7 +3036,7 @@ do_home:
        /*
         *      Call the pre-proxy routines.
         */
-       vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
        if (vp) {
                DICT_VALUE const *dval = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
                /* Must be a validation issue */
@@ -2854,6 +3064,7 @@ do_home:
        } else {
                rcode = process_pre_proxy(pre_proxy_type, request);
        }
+
        switch (rcode) {
        case RLM_MODULE_FAIL:
        case RLM_MODULE_INVALID:
@@ -2873,12 +3084,71 @@ do_home:
        case RLM_MODULE_NOOP:
        case RLM_MODULE_OK:
        case RLM_MODULE_UPDATED:
-               break;
+               return 1;
        }
+}
 
-       return 1;
+static int proxy_to_virtual_server(REQUEST *request)
+{
+       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);
+
+       /*
+        *      Packets to virtual servers 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(!spawn_flag || !we_are_master());
+
+       fake = request_alloc_fake(request);
+
+       fake->packet->vps = fr_pair_list_copy(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;
+
+       talloc_free(fake);
+
+       /*
+        *      No reply code, toss the reply we have,
+        *      and do post-proxy-type Fail.
+        */
+       if (!request->proxy_reply->code) {
+               TALLOC_FREE(request->proxy_reply);
+               setup_post_proxy_fail(request);
+       }
+
+       /*
+        *      Do the proxy reply (if any)
+        */
+       if (process_proxy_reply(request, request->proxy_reply)) {
+               request->handle(request);
+       }
+
+       return -1;      /* so we call request_finish */
 }
 
+
 static int request_proxy(REQUEST *request, int retransmit)
 {
        char buffer[128];
@@ -2908,83 +3178,19 @@ static int request_proxy(REQUEST *request, int retransmit)
         *
         *      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);
-
-               /*
-                *      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());
-
-               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;
-
-               talloc_free(fake);
-
-               /*
-                *      No reply code, toss the reply we have,
-                *      and do post-proxy-type Fail.
-                */
-               if (!request->proxy_reply->code) {
-                       TALLOC_FREE(request->proxy_reply);
-                       setup_post_proxy_fail(request);
-               }
-
-               /*
-                *      Just do the work here, rather than trying to
-                *      run the "decode proxy reply" stuff...
-                */
-               process_proxy_reply(request, request->proxy_reply);
-
-               /*
-                *      If we have a reply, run it through the handler.
-                */
-               if (request->proxy_reply) {
-                       request->handle(request); /* to do more post-proxy stuff */
-               }
-
-               return -1;      /* so we call request_finish */
-       }
+       if (request->home_server->server) return proxy_to_virtual_server(request);
 
        /*
         *      We're actually sending a proxied packet.  Do that now.
         */
        if (!request->in_proxy_hash && !insert_into_proxy_hash(request)) {
-               ERROR("Failed to insert request into the proxy list");
+               RPROXY("Failed to insert request into the proxy list");
                return -1;
        }
 
        rad_assert(request->proxy->id >= 0);
 
-       if (debug_flag) {
+       if (rad_debug_lvl) {
                struct timeval *response_window;
 
                response_window = request_response_window(request);
@@ -3012,14 +3218,33 @@ static int request_proxy(REQUEST *request, int retransmit)
        gettimeofday(&request->proxy_retransmit, NULL);
        if (!retransmit) {
                request->proxy->timestamp = request->proxy_retransmit;
-               request->home_server->last_packet_sent = request->proxy_retransmit.tv_sec;
        }
+       request->home_server->last_packet_sent = request->proxy_retransmit.tv_sec;
 
-       FR_STATS_TYPE_INC(request->home_server->stats.total_requests);
-       NO_CHILD_THREAD;
+       /*
+        *      Encode the packet before we do anything else.
+        */
+       request->proxy_listener->encode(request->proxy_listener, request);
+       debug_packet(request, request->proxy, false);
+
+       /*
+        *      Set the state function, then the state, no child, and
+        *      send the packet.
+        *
+        *      The order here is different from other state changes
+        *      due to race conditions with replies from the home
+        *      server.
+        */
+       request->process = proxy_wait_for_reply;
        request->child_state = REQUEST_PROXIED;
+       request->component = "<REQUEST_PROXIED>";
+       request->module = "";
+       NO_CHILD_THREAD;
+
+       /*
+        *      And send the packet.
+        */
        request->proxy_listener->send(request->proxy_listener, request);
-       debug_packet(request, request->proxy, false);
        return 1;
 }
 
@@ -3054,23 +3279,10 @@ static int request_proxy_anew(REQUEST *request)
                        request_queue_or_run(request, proxy_running);
                } else {
                        gettimeofday(&request->reply->timestamp, NULL);
-                       request_cleanup_delay_init(request, NULL);
+                       request_cleanup_delay_init(request);
                }
                return 0;
        }
-       home_server_update_request(home, request);
-
-       if (!insert_into_proxy_hash(request)) {
-               RPROXY("Failed to insert retransmission into the proxy list");
-               goto post_proxy_fail;
-       }
-
-       /*
-        *      Free the old packet, to force re-encoding
-        */
-       talloc_free(request->proxy->data);
-       request->proxy->data = NULL;
-       request->proxy->data_len = 0;
 
 #ifdef WITH_ACCOUNTING
        /*
@@ -3079,8 +3291,8 @@ static int request_proxy_anew(REQUEST *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->proxy,
+               vp = fr_pair_find_by_num(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
+               if (!vp) vp = radius_pair_create(request->proxy,
                                                &request->proxy->vps,
                                                PW_ACCT_DELAY_TIME, 0);
                if (vp) {
@@ -3090,14 +3302,45 @@ static int request_proxy_anew(REQUEST *request)
                        vp->vp_integer += now.tv_sec - request->proxy_retransmit.tv_sec;
                }
        }
-#endif
+#endif
+
+       /*
+        *      May have failed over to a "fallback" virtual server.
+        *      If so, run that instead of doing proxying to a real
+        *      server.
+        */
+       if (home->server) {
+               request->home_server = home;
+               TALLOC_FREE(request->proxy);
+
+               (void) proxy_to_virtual_server(request);
+               return 0;
+       }
+
+       home_server_update_request(home, request);
+
+       if (!insert_into_proxy_hash(request)) {
+               RPROXY("Failed to insert retransmission into the proxy list");
+               goto post_proxy_fail;
+       }
+
+       /*
+        *      Free the old packet, to force re-encoding
+        */
+       talloc_free(request->proxy->data);
+       request->proxy->data = NULL;
+       request->proxy->data_len = 0;
 
        if (request_proxy(request, 1) != 1) goto post_proxy_fail;
 
        return 1;
 }
 
-STATE_MACHINE_DECL(request_ping)
+
+/** Ping a home server.
+ *
+ */
+static void request_ping(REQUEST *request, int action)
 {
        home_server_t *home = request->home_server;
        char buffer[128];
@@ -3109,8 +3352,9 @@ STATE_MACHINE_DECL(request_ping)
 
        switch (action) {
        case FR_ACTION_TIMER:
-               ERROR("No response to status check %d for home server %s port %d",
+               ERROR("No response to status check %d ID %u for home server %s port %d",
                       request->number,
+                      request->proxy->id,
                       inet_ntop(request->proxy->dst_ipaddr.af,
                                 &request->proxy->dst_ipaddr.ipaddr,
                                 buffer, sizeof(buffer)),
@@ -3121,8 +3365,8 @@ STATE_MACHINE_DECL(request_ping)
                rad_assert(request->in_proxy_hash);
 
                request->home_server->num_received_pings++;
-               RPROXY("Received response to status check %d (%d in current sequence)",
-                      request->number, home->num_received_pings);
+               RPROXY("Received response to status check %d ID %u (%d in current sequence)",
+                      request->number, request->proxy->id, home->num_received_pings);
 
                /*
                 *      Remove the request from any hashes
@@ -3153,21 +3397,7 @@ STATE_MACHINE_DECL(request_ping)
                 *      Mark it alive and delete any outstanding
                 *      pings.
                 */
-               home->state = HOME_STATE_ALIVE;
-               home->response_timeouts = 0;
-               exec_trigger(request, home->cs, "home_server.alive", false);
-               home->currently_outstanding = 0;
-               home->num_sent_pings = 0;
-               home->num_received_pings = 0;
-               gettimeofday(&home->revive_time, NULL);
-
-               fr_event_delete(el, &home->ev);
-
-               RPROXY("Marking home server %s port %d alive",
-                      inet_ntop(request->proxy->dst_ipaddr.af,
-                                &request->proxy->dst_ipaddr.ipaddr,
-                                buffer, sizeof(buffer)),
-                      request->proxy->dst_port);
+               mark_home_server_alive(request, home);
                break;
 
        default:
@@ -3177,6 +3407,7 @@ STATE_MACHINE_DECL(request_ping)
 
        rad_assert(!request->in_request_hash);
        rad_assert(request->ev == NULL);
+       NO_CHILD_THREAD;
        request_done(request, FR_ACTION_DONE);
 }
 
@@ -3200,6 +3431,7 @@ static void ping_home_server(void *ctx)
        }
 
        gettimeofday(&now, NULL);
+       ASSERT_MASTER;
 
        /*
         *      We've run out of zombie time.  Mark it dead.
@@ -3220,8 +3452,8 @@ static void ping_home_server(void *ctx)
         */
        if (home->ping_check == HOME_PING_CHECK_NONE) {
                if (home->state == HOME_STATE_ZOMBIE) {
-                       when = home->zombie_period_start;
-                       when.tv_sec += home->zombie_period;
+                       home->when = home->zombie_period_start;
+                       home->when.tv_sec += home->zombie_period;
                        INSERT_EVENT(ping_home_server, home);
                }
 
@@ -3244,32 +3476,32 @@ static void ping_home_server(void *ctx)
        if (home->ping_check == HOME_PING_CHECK_STATUS_SERVER) {
                request->proxy->code = PW_CODE_STATUS_SERVER;
 
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "Message-Authenticator", "0x00", T_OP_SET);
 
        } else if (home->type == HOME_TYPE_AUTH) {
                request->proxy->code = PW_CODE_ACCESS_REQUEST;
 
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "User-Name", home->ping_user_name, T_OP_SET);
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "User-Password", home->ping_user_password, T_OP_SET);
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "Service-Type", "Authenticate-Only", T_OP_SET);
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "Message-Authenticator", "0x00", T_OP_SET);
 
        } else {
 #ifdef WITH_ACCOUNTING
                request->proxy->code = PW_CODE_ACCOUNTING_REQUEST;
 
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "User-Name", home->ping_user_name, T_OP_SET);
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "Acct-Status-Type", "Stop", T_OP_SET);
-               pairmake(request->proxy, &request->proxy->vps,
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "Acct-Session-Id", "00000000", T_OP_SET);
-               vp = pairmake(request->proxy, &request->proxy->vps,
+               vp = fr_pair_make(request->proxy, &request->proxy->vps,
                              "Event-Timestamp", "0", T_OP_SET);
                vp->vp_date = now.tv_sec;
 #else
@@ -3277,27 +3509,30 @@ static void ping_home_server(void *ctx)
 #endif
        }
 
-       vp = pairmake(request->proxy, &request->proxy->vps,
+       vp = fr_pair_make(request->proxy, &request->proxy->vps,
                      "NAS-Identifier", "", T_OP_SET);
        if (vp) {
-               pairsprintf(vp, "Status Check %u. Are you alive?",
+               fr_pair_value_sprintf(vp, "Status Check %u. Are you alive?",
                            home->num_sent_pings);
        }
 
+#ifdef WITH_TCP
+       request->proxy->proto = home->proto;
+#endif
        request->proxy->src_ipaddr = home->src_ipaddr;
        request->proxy->dst_ipaddr = home->ipaddr;
        request->proxy->dst_port = home->port;
        request->home_server = home;
 #ifdef DEBUG_STATE_MACHINE
-       if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
+       if (rad_debug_lvl) 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");
+       if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_ping");
 #endif
 #ifdef HAVE_PTHREAD_H
        rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
 #endif
-       request->child_state = REQUEST_DONE;
+       request->child_state = REQUEST_PROXIED;
        request->process = request_ping;
 
        rad_assert(request->proxy_listener == NULL);
@@ -3326,6 +3561,7 @@ static void ping_home_server(void *ctx)
        home->num_sent_pings++;
 
        rad_assert(request->proxy_listener != NULL);
+       debug_packet(request, request->proxy, false);
        request->proxy_listener->send(request->proxy_listener,
                                      request);
 
@@ -3385,7 +3621,7 @@ static void mark_home_server_zombie(home_server_t *home, struct timeval *now, st
         */
        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.",
+               DEBUG("Received reply from home server %d seconds ago.  Might not be zombie.",
                      (int) (now->tv_sec - home->last_packet_recv));
                return;
        }
@@ -3401,6 +3637,7 @@ static void mark_home_server_zombie(home_server_t *home, struct timeval *now, st
        home->zombie_period_start.tv_usec = USEC / 2;
 
        fr_event_delete(el, &home->ev);
+
        home->num_sent_pings = 0;
        home->num_received_pings = 0;
 
@@ -3431,6 +3668,7 @@ void revive_home_server(void *ctx)
        /*
         *      Delete any outstanding events.
         */
+       ASSERT_MASTER;
        if (home->ev) fr_event_delete(el, &home->ev);
 
        PROXY( "Marking home server %s port %d alive again... we have no idea if it really is alive or not.",
@@ -3480,11 +3718,31 @@ void mark_home_server_dead(home_server_t *home, struct timeval *when)
                home->when.tv_sec += home->revive_interval;
 
                DEBUG("PING: Reviving home server %s in %u seconds", home->log_name, home->revive_interval);
+               ASSERT_MASTER;
                INSERT_EVENT(revive_home_server, home);
        }
 }
 
-STATE_MACHINE_DECL(proxy_wait_for_reply)
+/** Wait for a reply after proxying a request.
+ *
+ *  Retransmit the proxied packet, or time out and go to
+ *  proxy_no_reply.  Mark the home server unresponsive, etc.
+ *
+ *  If we do receive a reply, we transition to proxy_running.
+ *
+ *  \dot
+ *     digraph proxy_wait_for_reply {
+ *             proxy_wait_for_reply;
+ *
+ *             proxy_wait_for_reply -> retransmit_proxied_request [ label = "DUP", arrowhead = "none" ];
+ *             proxy_wait_for_reply -> proxy_no_reply [ label = "TIMER >= response_window" ];
+ *             proxy_wait_for_reply -> timer [ label = "TIMER < max_request_time" ];
+ *             proxy_wait_for_reply -> proxy_running [ label = "PROXY_REPLY" arrowhead = "none"];
+ *             proxy_wait_for_reply -> done [ label = "TIMER >= max_request_time" ];
+ *     }
+ *  \enddot
+ */
+static void proxy_wait_for_reply(REQUEST *request, int action)
 {
        struct timeval now, when;
        struct timeval *response_window = NULL;
@@ -3494,15 +3752,11 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       CHECK_FOR_STOP;
 
        rad_assert(request->packet->code != PW_CODE_STATUS_SERVER);
        rad_assert(request->home_server != NULL);
 
-       if (request->master_state == REQUEST_STOP_PROCESSING) {
-               request->child_state = REQUEST_DONE;
-               return;
-       }
-
        gettimeofday(&now, NULL);
 
        switch (action) {
@@ -3518,14 +3772,27 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                 */
                if (request->home_server->server) return;
 
+               /*
+                *      Use a new connection when the home server is
+                *      dead, or when there's no proxy listener, or
+                *      when the listener is failed or dead.
+                *
+                *      If the listener is known or frozen, use it for
+                *      retransmits.
+                */
                if ((home->state == HOME_STATE_IS_DEAD) ||
                    !request->proxy_listener ||
-                   (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
+                   (request->proxy_listener->status >= RAD_LISTEN_STATUS_EOL)) {
                        request_proxy_anew(request);
                        return;
                }
 
 #ifdef WITH_TCP
+               /*
+                *      The home server is still alive, but TCP.  We
+                *      rely on TCP to get the request and reply back.
+                *      So there's no need to retransmit.
+                */
                if (home->proto == IPPROTO_TCP) {
                        DEBUG2("Suppressing duplicate proxied request (tcp) to home server %s port %d proto TCP - ID: %d",
                               inet_ntop(request->proxy->dst_ipaddr.af,
@@ -3560,7 +3827,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                 *      get a new ID.
                 */
                if ((request->packet->code == PW_CODE_ACCOUNTING_REQUEST) &&
-                   pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY)) {
+                   fr_pair_find_by_num(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY)) {
                        request_proxy_anew(request);
                        return;
                }
@@ -3578,8 +3845,8 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                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);
                debug_packet(request, request->proxy, false);
+               request->proxy_listener->send(request->proxy_listener, request);
                break;
 
        case FR_ACTION_TIMER:
@@ -3587,7 +3854,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 
 #ifdef WITH_TCP
                if (!request->proxy_listener ||
-                   (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
+                   (request->proxy_listener->status >= RAD_LISTEN_STATUS_EOL)) {
                        remove_from_proxy_hash(request);
 
                        when = request->packet->timestamp;
@@ -3695,23 +3962,17 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                        request_queue_or_run(request, proxy_no_reply);
                } else {
                        gettimeofday(&request->reply->timestamp, NULL);
-                       request_cleanup_delay_init(request, NULL);
+                       request_cleanup_delay_init(request);
                }
                break;
 
                /*
-                *      Duplicate proxy replies have been quenched by
-                *      now.  This state is only called ONCE, when we
-                *      receive a new reply from the home server.
+                *      We received a new reply.  Go process it.
                 */
        case FR_ACTION_PROXY_REPLY:
                request_queue_or_run(request, proxy_running);
                break;
 
-       case FR_ACTION_CONFLICTING:
-               request_done(request, action);
-               return;
-
        default:
                RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
                break;
@@ -3719,6 +3980,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 }
 #endif /* WITH_PROXY */
 
+
 /***********************************************************************
  *
  *  CoA code
@@ -3751,9 +4013,9 @@ static void request_coa_originate(REQUEST *request)
        /*
         *      Check whether we want to originate one, or cancel one.
         */
-       vp = pairfind(request->config_items, PW_SEND_COA_REQUEST, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_SEND_COA_REQUEST, 0, TAG_ANY);
        if (!vp) {
-               vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->coa->proxy->vps, PW_SEND_COA_REQUEST, 0, TAG_ANY);
        }
 
        if (vp) {
@@ -3770,16 +4032,16 @@ static void request_coa_originate(REQUEST *request)
         *      src_ipaddr will be set up in proxy_encode.
         */
        memset(&ipaddr, 0, sizeof(ipaddr));
-       vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
        if (vp) {
                ipaddr.af = AF_INET;
                ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                ipaddr.prefix = 32;
-       } else if ((vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL) {
+       } else if ((vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL) {
                ipaddr.af = AF_INET6;
                ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
                ipaddr.prefix = 128;
-       } else if ((vp = pairfind(coa->proxy->vps, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
+       } else if ((vp = fr_pair_find_by_num(coa->proxy->vps, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
                coa->home_pool = home_pool_byname(vp->vp_strvalue,
                                                  HOME_TYPE_COA);
                if (!coa->home_pool) {
@@ -3819,7 +4081,7 @@ static void request_coa_originate(REQUEST *request)
        } else if (!coa->home_server) {
                uint16_t port = PW_COA_UDP_PORT;
 
-               vp = pairfind(coa->proxy->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
                if (vp) port = vp->vp_integer;
 
                coa->home_server = home_server_find(&ipaddr, port, IPPROTO_UDP);
@@ -3831,7 +4093,7 @@ static void request_coa_originate(REQUEST *request)
                }
        }
 
-       vp = pairfind(coa->proxy->vps, PW_PACKET_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_TYPE, 0, TAG_ANY);
        if (vp) {
                switch (vp->vp_integer) {
                case PW_CODE_COA_REQUEST:
@@ -3859,7 +4121,7 @@ static void request_coa_originate(REQUEST *request)
        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->config = fr_pair_list_copy(coa, request->config);
        coa->num_coa_requests = 0;
        coa->handle = null_handler;
        coa->number = request->number; /* it's associated with the same request */
@@ -3867,7 +4129,7 @@ static void request_coa_originate(REQUEST *request)
        /*
         *      Call the pre-proxy routines.
         */
-       vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
        if (vp) {
                DICT_VALUE const *dval = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
                /* Must be a validation issue */
@@ -3925,6 +4187,7 @@ static void request_coa_originate(REQUEST *request)
         */
        gettimeofday(&coa->proxy->timestamp, NULL);
        coa->packet->timestamp = coa->proxy->timestamp; /* for max_request_time */
+       coa->home_server->last_packet_sent = coa->proxy->timestamp.tv_sec;
        coa->delay = 0;         /* need to calculate a new delay */
 
        /*
@@ -3933,38 +4196,47 @@ static void request_coa_originate(REQUEST *request)
         */
        fr_state_put_vps(coa, NULL, coa->packet);
 
-       coa->process = coa_wait_for_reply;
+       /*
+        *      Encode the packet before we do anything else.
+        */
+       coa->proxy_listener->encode(coa->proxy_listener, coa);
+       debug_packet(coa, coa->proxy, false);
+
 #ifdef DEBUG_STATE_MACHINE
-       if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
+       if (rad_debug_lvl) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
                               child_state_names[request->child_state],
-                              child_state_names[REQUEST_RUNNING]);
+                              child_state_names[REQUEST_PROXIED]);
 #endif
+
+       /*
+        *      Set the state function, then the state, no child, and
+        *      send the packet.
+        */
+       coa->process = coa_wait_for_reply;
+       coa->child_state = REQUEST_PROXIED;
+
 #ifdef HAVE_PTHREAD_H
        coa->child_pid = NO_SUCH_CHILD_PID;
 #endif
-       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;
+
+       if (we_are_master()) coa_separate(request->coa);
+
+       /*
+        *      And send the packet.
+        */
        coa->proxy_listener->send(coa->proxy_listener, coa);
-       debug_packet(coa, coa->proxy, false);
 }
 
 
-static void coa_timer(REQUEST *request)
+static void coa_retransmit(REQUEST *request)
 {
        uint32_t delay, frac;
        struct timeval now, when, mrd;
+       char buffer[128];
 
        VERIFY_REQUEST(request);
 
-       rad_assert(request->parent == NULL);
-
-       if (request->proxy_reply) {
-               request_process_timer(request);
-               return;
-       }
-       gettimeofday(&now, NULL);
+       fr_event_now(el, &now);
 
        if (request->delay == 0) {
                /*
@@ -4002,8 +4274,6 @@ static void coa_timer(REQUEST *request)
         */
        if (request->home_server->coa_mrc &&
            (request->num_coa_requests >= request->home_server->coa_mrc)) {
-               char buffer[128];
-
                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,
@@ -4070,47 +4340,54 @@ static void coa_timer(REQUEST *request)
 
        FR_STATS_TYPE_INC(request->home_server->stats.total_requests);
 
-       /*
-        *      Status servers don't count as real packets sent.
-        */
+       RDEBUG2("Sending duplicate CoA request to home server %s port %d - ID: %d",
+               inet_ntop(request->proxy->dst_ipaddr.af,
+                         &request->proxy->dst_ipaddr.ipaddr,
+                         buffer, sizeof(buffer)),
+               request->proxy->dst_port,
+               request->proxy->id);
+
        request->proxy_listener->send(request->proxy_listener,
                                      request);
 }
 
-STATE_MACHINE_DECL(coa_wait_for_reply)
+
+/** Wait for a reply after originating a CoA a request.
+ *
+ *  Retransmit the proxied packet, or time out and go to
+ *  coa_no_reply.  Mark the home server unresponsive, etc.
+ *
+ *  If we do receive a reply, we transition to coa_running.
+ *
+ *  \dot
+ *     digraph coa_wait_for_reply {
+ *             coa_wait_for_reply;
+ *
+ *             coa_wait_for_reply -> coa_no_reply [ label = "TIMER >= response_window" ];
+ *             coa_wait_for_reply -> timer [ label = "TIMER < max_request_time" ];
+ *             coa_wait_for_reply -> coa_running [ label = "PROXY_REPLY" arrowhead = "none"];
+ *             coa_wait_for_reply -> done [ label = "TIMER >= max_request_time" ];
+ *     }
+ *  \enddot
+ */
+static void coa_wait_for_reply(REQUEST *request, int action)
 {
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       ASSERT_MASTER;
+       CHECK_FOR_STOP;
+
+       if (request->parent) coa_separate(request);
 
        switch (action) {
        case FR_ACTION_TIMER:
-               /*
-                *      This is big enough to be in it's own function.
-                */
-               coa_timer(request);
+               if (request_max_time(request)) break;
+
+               coa_retransmit(request);
                break;
 
        case FR_ACTION_PROXY_REPLY:
-               rad_assert(request->parent != NULL);
-               rad_assert(request->parent->coa == request);
-               rad_assert((request->proxy->code == PW_CODE_COA_REQUEST) ||
-                          (request->proxy->code == PW_CODE_DISCONNECT_REQUEST));
-               rad_assert(request->process != NULL);
-
-               coa_separate(request, FR_ACTION_PROXY_REPLY);
-
-               rad_assert(request->parent == NULL);
-
-               /*
-                *      Do NOT get the session-state VPs.  The request
-                *      already contains the packet and the reply, so
-                *      there's no more state we need to maintain.
-                *
-                *      The state for "originate CoA" is for the next
-                *      Access-Request, not for the CoA ACK/BAK
-                */
-
                request_queue_or_run(request, coa_running);
                break;
 
@@ -4120,11 +4397,15 @@ STATE_MACHINE_DECL(coa_wait_for_reply)
        }
 }
 
-STATE_MACHINE_DECL(coa_separate)
+static void coa_separate(REQUEST *request)
 {
        VERIFY_REQUEST(request);
+#ifdef DEBUG_STATE_MACHINE
+       int action = FR_ACTION_TIMER;
+#endif
 
        TRACE_STATE_MACHINE;
+       ASSERT_MASTER;
 
        rad_assert(request->parent != NULL);
        rad_assert(request->parent->coa == request);
@@ -4138,39 +4419,41 @@ STATE_MACHINE_DECL(coa_separate)
        request->parent->coa = NULL;
        request->parent = NULL;
 
-       /*
-        *      Most of the time we're called for timers.
-        */
-       switch (action) {
-       case FR_ACTION_TIMER:
-               request->process(request, FR_ACTION_TIMER);
-               break;
-
-               /*
-                *      Set up the main timers.
-                */
-       case FR_ACTION_PROXY_REPLY:
-               request->child_state = REQUEST_QUEUED;
-               request_process_timer(request);
-               break;
-
-       default:
-               RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
-               break;
+       if (we_are_master()) {
+               request->delay = 0;
+               coa_retransmit(request);
        }
 }
 
-STATE_MACHINE_DECL(coa_no_reply)
+
+/** Process a request after the CoA has timed out.
+ *
+ *  Run the packet through Post-Proxy-Type Fail
+ *
+ *  \dot
+ *     digraph coa_no_reply {
+ *             coa_no_reply;
+ *
+ *             coa_no_reply -> dup [ label = "DUP", arrowhead = "none" ];
+ *             coa_no_reply -> timer [ label = "TIMER < max_request_time" ];
+ *             coa_no_reply -> coa_reply_too_late [ label = "PROXY_REPLY" arrowhead = "none"];
+ *             coa_no_reply -> process_proxy_reply [ label = "RUN" ];
+ *             coa_no_reply -> done [ label = "TIMER >= timeout" ];
+ *     }
+ *  \enddot
+ */
+static void coa_no_reply(REQUEST *request, int action)
 {
        char buffer[128];
 
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       CHECK_FOR_STOP;
 
        switch (action) {
        case FR_ACTION_TIMER:
-               request_common(request, action);
+               (void) request_max_time(request);
                break;
 
        case FR_ACTION_PROXY_REPLY: /* too late! */
@@ -4182,10 +4465,9 @@ STATE_MACHINE_DECL(coa_no_reply)
                break;
 
        case FR_ACTION_RUN:
-               /*
-                *      FIXME: do recv_coa Fail
-                */
-               (void) process_proxy_reply(request, NULL);
+               if (process_proxy_reply(request, NULL)) {
+                       request->handle(request);
+               }
                request_done(request, FR_ACTION_DONE);
                break;
 
@@ -4195,28 +4477,39 @@ STATE_MACHINE_DECL(coa_no_reply)
        }
 }
 
-STATE_MACHINE_DECL(coa_running)
+
+/** Process the request after receiving a coa reply.
+ *
+ *  Throught the post-proxy section, and the through the handler
+ *  function.
+ *
+ *  \dot
+ *     digraph coa_running {
+ *             coa_running;
+ *
+ *             coa_running -> timer [ label = "TIMER < max_request_time" ];
+ *             coa_running -> process_proxy_reply [ label = "RUN" ];
+ *             coa_running -> done [ label = "TIMER >= timeout" ];
+ *     }
+ *  \enddot
+ */
+static void coa_running(REQUEST *request, int action)
 {
        VERIFY_REQUEST(request);
 
        TRACE_STATE_MACHINE;
+       CHECK_FOR_STOP;
 
        switch (action) {
        case FR_ACTION_TIMER:
-               request_process_timer(request);
-               break;
-
-       case FR_ACTION_PROXY_REPLY:
-               request_common(request, action);
+               (void) request_max_time(request);
                break;
 
        case FR_ACTION_RUN:
                if (process_proxy_reply(request, request->proxy_reply)) {
                        request->handle(request);
-                       request_finish(request, action);
-               } else {
-                       request_done(request, FR_ACTION_DONE);
                }
+               request_done(request, FR_ACTION_DONE);
                break;
 
        default:
@@ -4294,6 +4587,7 @@ static void event_poll_detail(void *ctx)
 
        tv_add(&when, delay);
 
+       ASSERT_MASTER;
        if (!fr_event_insert(el, event_poll_detail, this,
                             &when, &detail->ev)) {
                ERROR("Failed creating handler");
@@ -4309,7 +4603,7 @@ static void event_status(struct timeval *wake)
        int argval;
 #endif
 
-       if (debug_flag == 0) {
+       if (rad_debug_lvl == 0) {
                if (just_started) {
                        INFO("Ready to process requests");
                        just_started = false;
@@ -4359,6 +4653,7 @@ static void listener_free_cb(void *ctx)
                fr_event_now(el, &when);
                when.tv_sec += 3;
 
+               ASSERT_MASTER;
                if (!fr_event_insert(el, listener_free_cb, this, &when,
                                     &(sock->ev))) {
                        rad_panic("Failed to insert event");
@@ -4434,11 +4729,14 @@ static int event_new_fd(rad_listen_t *this)
                rad_assert(sock != NULL);
                if (just_started) {
                        DEBUG("Listening on %s", buffer);
+               } else {
+                       INFO(" ... adding new socket %s", buffer);
+               }
 
 #ifdef WITH_PROXY
-               } else if (this->type == RAD_LISTEN_PROXY) {
+               if (!just_started && (this->type == RAD_LISTEN_PROXY)) {
                        home_server_t *home;
-
+                       
                        home = sock->home;
                        if (!home || !home->limit.max_connections) {
                                INFO(" ... adding new socket %s", buffer);
@@ -4448,8 +4746,6 @@ static int event_new_fd(rad_listen_t *this)
                        }
 
 #endif
-               } else {
-                       INFO(" ... adding new socket %s", buffer);
                }
 
                switch (this->type) {
@@ -4492,6 +4788,7 @@ static int event_new_fd(rad_listen_t *this)
                                when.tv_sec = sock->opened + 1;
                                when.tv_usec = 0;
 
+                               ASSERT_MASTER;
                                if (!fr_event_insert(el, tcp_socket_timer, this, &when,
                                                     &(sock->ev))) {
                                        rad_panic("Failed to insert event");
@@ -4517,6 +4814,7 @@ static int event_new_fd(rad_listen_t *this)
                                when.tv_sec = sock->opened + 1;
                                when.tv_usec = 0;
 
+                               ASSERT_MASTER;
                                if (!fr_event_insert(el, tcp_socket_timer, this, &when,
                                                     &(sock->ev))) {
                                        ERROR("Failed adding timer for socket: %s", fr_strerror());
@@ -4542,7 +4840,40 @@ static int event_new_fd(rad_listen_t *this)
 
 #ifdef WITH_TCP
        /*
-        *      Stop using this socket, if at all possible.
+        *      The socket has reached a timeout.  Try to close it.
+        */
+       if (this->status == RAD_LISTEN_STATUS_FROZEN) {
+               /*
+                *      Requests are still using the socket.  Wait for
+                *      them to finish.
+                */
+               if (this->count > 0) {
+                       struct timeval when;
+                       listen_socket_t *sock = this->data;
+
+                       /*
+                        *      Try again to clean up the socket in 30
+                        *      seconds.
+                        */
+                       gettimeofday(&when, NULL);
+                       when.tv_sec += 30;
+
+                       ASSERT_MASTER;
+                       if (!fr_event_insert(el,
+                                            (fr_event_callback_t) event_new_fd,
+                                            this, &when, &sock->ev)) {
+                               rad_panic("Failed to insert event");
+                       }
+
+                       return 1;
+               }
+
+               fr_event_fd_delete(el, 0, this->fd);
+               this->status = RAD_LISTEN_STATUS_REMOVE_NOW;
+       }
+
+       /*
+        *      The socket has had a catastrophic error.  Close it.
         */
        if (this->status == RAD_LISTEN_STATUS_EOL) {
                /*
@@ -4552,10 +4883,7 @@ static int event_new_fd(rad_listen_t *this)
 
 #ifdef WITH_PROXY
                /*
-                *      Proxy sockets get frozen, so that we don't use
-                *      them for new requests.  But we do keep them
-                *      open to listen for replies to requests we had
-                *      previously sent.
+                *      Tell all requests using this socket that the socket is dead.
                 */
                if (this->type == RAD_LISTEN_PROXY) {
                        PTHREAD_MUTEX_LOCK(&proxy_mutex);
@@ -4565,7 +4893,9 @@ static int event_new_fd(rad_listen_t *this)
                                fr_exit(1);
                        }
 
-                       fr_packet_list_walk(proxy_list, this, proxy_eol_cb);
+                       if (this->count > 0) {
+                               fr_packet_list_walk(proxy_list, this, proxy_eol_cb);
+                       }
                        PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
                }
 #endif
@@ -4585,6 +4915,7 @@ static int event_new_fd(rad_listen_t *this)
                        gettimeofday(&when, NULL);
                        when.tv_sec += 30;
 
+                       ASSERT_MASTER;
                        if (!fr_event_insert(el,
                                             (fr_event_callback_t) event_new_fd,
                                             this, &when, &sock->ev)) {
@@ -4684,6 +5015,7 @@ static int event_new_fd(rad_listen_t *this)
                 *      No child threads, clean it up now.
                 */
                if (!spawn_flag) {
+                       ASSERT_MASTER;
                        if (sock->ev) fr_event_delete(el, &sock->ev);
                        listen_free(&this);
                        return 1;
@@ -4695,6 +5027,7 @@ static int event_new_fd(rad_listen_t *this)
                gettimeofday(&when, NULL);
                when.tv_sec += 3;
 
+               ASSERT_MASTER;
                if (!fr_event_insert(el, listener_free_cb, this, &when,
                                     &(sock->ev))) {
                        rad_panic("Failed to insert event");
@@ -4748,8 +5081,7 @@ static void handle_signal_self(int flag)
                fr_event_loop_exit(el, 0x80);
        }
 
-#ifdef WITH_DETAIL
-#ifndef WITH_DETAIL_THREAD
+#if defined(WITH_DETAIL) && !defined(WITH_DETAIL_THREAD)
        if ((flag & RADIUS_SIGNAL_SELF_DETAIL) != 0) {
                rad_listen_t *this;
 
@@ -4774,11 +5106,8 @@ static void handle_signal_self(int flag)
                }
        }
 #endif
-#endif
 
-#ifdef WITH_TCP
-#ifdef WITH_PROXY
-#ifdef HAVE_PTHREAD_H
+#if defined(WITH_TCP) && defined(WITH_PROXY) && defined(HAVE_PTHREAD_H)
        /*
         *      There are new listeners in the list.  Run
         *      event_new_fd() on them.
@@ -4802,9 +5131,7 @@ static void handle_signal_self(int flag)
                new_listeners = NULL;
                FD_MUTEX_UNLOCK(&fd_mutex);
        }
-#endif /* HAVE_PTHREAD_H */
-#endif /* WITH_PROXY */
-#endif /* WITH_TCP */
+#endif
 }
 
 #ifndef HAVE_PTHREAD_H
@@ -4888,6 +5215,119 @@ static int packet_entry_cmp(void const *one, void const *two)
        return fr_packet_cmp(*a, *b);
 }
 
+#ifdef WITH_PROXY
+/*
+ *     They haven't defined a proxy listener.  Automatically
+ *     add one for them, with the correct address family.
+ */
+static void create_default_proxy_listener(int af)
+{
+       uint16_t        port = 0;
+       home_server_t   home;
+       listen_socket_t *sock;
+       rad_listen_t    *this;
+
+       memset(&home, 0, sizeof(home));
+
+       /*
+        *      Open a default UDP port
+        */
+       home.proto = IPPROTO_UDP;
+       port = 0;
+
+       /*
+        *      Set the address family.
+        */
+       home.src_ipaddr.af = af;
+       home.ipaddr.af = af;
+
+       /*
+        *      Get the correct listener.
+        */
+       this = proxy_new_listener(proxy_ctx, &home, port);
+       if (!this) {
+               fr_exit_now(1);
+       }
+
+       sock = this->data;
+       if (!fr_packet_list_socket_add(proxy_list, this->fd,
+                                      sock->proto,
+                                      &sock->other_ipaddr, sock->other_port,
+                                      this)) {
+               ERROR("Failed adding proxy socket");
+               fr_exit_now(1);
+       }
+
+       /*
+        *      Insert the FD into list of FDs to listen on.
+        */
+       radius_update_listener(this);
+}
+
+/*
+ *     See if we automatically need to open a proxy socket.
+ */
+static void check_proxy(rad_listen_t *head)
+{
+       bool            defined_proxy;
+       bool            has_v4, has_v6;
+       rad_listen_t    *this;
+
+       if (check_config) return;
+       if (!main_config.proxy_requests) return;
+       if (!head) return;
+       if (!home_servers_udp) return;
+
+       /*
+        *      We passed "-i" on the command line.  Use that address
+        *      family for the proxy socket.
+        */
+       if (main_config.myip.af != AF_UNSPEC) {
+               create_default_proxy_listener(main_config.myip.af);
+               return;
+       }
+
+       defined_proxy = has_v4 = has_v6 = false;
+
+       /*
+        *      Figure out if we need to open a proxy socket, and if
+        *      so, which one.
+        */
+       for (this = head; this != NULL; this = this->next) {
+               listen_socket_t *sock;
+
+               switch (this->type) {
+               case RAD_LISTEN_PROXY:
+                       defined_proxy = true;
+                       break;
+
+               case RAD_LISTEN_AUTH:
+#ifdef WITH_ACCT
+               case RAD_LISTEN_ACCT:
+#endif
+#ifdef WITH_COA
+               case RAD_LISTEN_COA:
+#endif
+                       sock = this->data;
+                       if (sock->my_ipaddr.af == AF_INET) has_v4 = true;
+                       if (sock->my_ipaddr.af == AF_INET6) has_v6 = true;
+                       break;
+                       
+               default:
+                       break;
+               }
+       }
+
+       /*
+        *      Assume they know what they're doing.
+        */
+       if (defined_proxy) return;
+
+       if (has_v4) create_default_proxy_listener(AF_INET);
+
+       if (has_v6) create_default_proxy_listener(AF_INET6);
+}
+#endif
 
 int radius_event_start(CONF_SECTION *cs, bool have_children)
 {
@@ -4910,7 +5350,7 @@ int radius_event_start(CONF_SECTION *cs, bool have_children)
        request_num_counter = 0;
 
 #ifdef WITH_PROXY
-       if (main_config.proxy_requests) {
+       if (main_config.proxy_requests && !check_config) {
                /*
                 *      Create the tree for managing proxied requests and
                 *      responses.
@@ -4936,6 +5376,7 @@ int radius_event_start(CONF_SECTION *cs, bool have_children)
                main_config.init_delay.tv_usec >>= 1;
                main_config.init_delay.tv_sec >>= 1;
 
+               proxy_ctx = talloc_init("proxy");
        }
 #endif
 
@@ -4996,23 +5437,27 @@ int radius_event_start(CONF_SECTION *cs, bool have_children)
        }
 #endif
 
-       DEBUG("%s: #### Opening IP addresses and Ports ####", main_config.name);
+       DEBUG("%s: #### Opening IP addresses and Ports ####", main_config.name);
 
-       /*
-             The server temporarily switches to an unprivileged
-             user very early in the bootstrapping process.
-             However, some sockets MAY require privileged access
-             (bind to device, or to port < 1024, or to raw
-             sockets).  Those sockets need to call suid up/down
-             themselves around the functions that need a privileged
-             uid.
-       */
-       if (listen_init(cs, &head, spawn_flag) < 0) {
+       /*
+        *      The server temporarily switches to an unprivileged
+        *      user very early in the bootstrapping process.
+        *      However, some sockets MAY require privileged access
+        *      (bind to device, or to port < 1024, or to raw
+        *      sockets).  Those sockets need to call suid up/down
+        *      themselves around the functions that need a privileged
+        *      uid.
+        */
+       if (listen_init(cs, &head, spawn_flag) < 0) {
                fr_exit_now(1);
        }
 
        main_config.listen = head;
 
+#ifdef WITH_PROXY
+       check_proxy(head);
+#endif
+
        /*
         *      At this point, no one has any business *ever* going
         *      back to root uid.
@@ -5080,6 +5525,7 @@ static int request_delete_cb(UNUSED void *ctx, void *data)
 #endif
 
        request->in_request_hash = false;
+       ASSERT_MASTER;
        if (request->ev) fr_event_delete(el, &request->ev);
 
        if (main_config.memory_report) {
@@ -5159,6 +5605,8 @@ void radius_event_free(void)
 #ifdef WITH_PROXY
        fr_packet_list_free(proxy_list);
        proxy_list = NULL;
+
+       if (proxy_ctx) talloc_free(proxy_ctx);
 #endif
 
        TALLOC_FREE(el);
index a682249..3badef9 100644 (file)
@@ -30,6 +30,7 @@ typedef struct REQUEST REQUEST;
 #include <freeradius-devel/xlat.h>
 #include <freeradius-devel/conf.h>
 #include <freeradius-devel/radpaths.h>
+#include <freeradius-devel/dhcp.h>
 
 #include <ctype.h>
 
@@ -40,8 +41,7 @@ typedef struct REQUEST REQUEST;
 #include <assert.h>
 
 #include <freeradius-devel/log.h>
-extern log_lvl_t debug_flag;
-log_lvl_t debug_flag = 0;
+extern log_lvl_t rad_debug_lvl;
 
 #include <sys/wait.h>
 pid_t rad_fork(void);
@@ -63,6 +63,24 @@ static ssize_t xlat_test(UNUSED void *instance, UNUSED REQUEST *request,
        return 0;
 }
 
+static RADIUS_PACKET my_original = {
+       .sockfd = -1,
+       .id = 0,
+       .code = PW_CODE_ACCESS_REQUEST,
+       .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+};
+
+
+static RADIUS_PACKET my_packet = {
+       .sockfd = -1,
+       .id = 0,
+       .code = PW_CODE_ACCESS_ACCEPT,
+       .vector = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+};
+
+
+static char const *my_secret = "testing123";
+
 /*
  *     End of hacks for xlat
  *
@@ -590,10 +608,11 @@ static void process_file(const char *root_dir, char const *filename)
 
        while (fgets(buffer, sizeof(buffer), fp) != NULL) {
                char *p = strchr(buffer, '\n');
-               VALUE_PAIR *vp, *head = NULL;
+               VALUE_PAIR *vp, *head;
                VALUE_PAIR **tail = &head;
 
                lineno++;
+               head = NULL;
 
                if (!p) {
                        if (!feof(fp)) {
@@ -668,17 +687,21 @@ static void process_file(const char *root_dir, char const *filename)
                                p += 7;
                        }
 
-                       if (userparse(NULL, p, &head) != T_EOL) {
+                       if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
                                strlcpy(output, fr_strerror(), sizeof(output));
                                continue;
                        }
 
                        attr = data;
                        vp = head;
-                       len = 0;
                        while (vp) {
-                               len = rad_vp2attr(NULL, NULL, NULL, (VALUE_PAIR const **)(void **)&vp,
-                                                 attr, sizeof(data) - (attr - data));
+                               VALUE_PAIR **pvp = &vp;
+                               VALUE_PAIR const **qvp;
+
+                               memcpy(&qvp, &pvp, sizeof(pvp));
+
+                               len = rad_vp2attr(&my_packet, &my_original, my_secret, qvp,
+                                                 attr, data + sizeof(data) - attr);
                                if (len < 0) {
                                        fprintf(stderr, "Failed encoding %s: %s\n",
                                                vp->da->name, fr_strerror());
@@ -689,8 +712,8 @@ static void process_file(const char *root_dir, char const *filename)
                                if (len == 0) break;
                        }
 
-                       pairfree(&head);
-                       outlen = len;
+                       fr_pair_list_free(&head);
+                       outlen = attr - data;
                        goto print_hex;
                }
 
@@ -712,9 +735,9 @@ static void process_file(const char *root_dir, char const *filename)
                        my_len = 0;
                        while (len > 0) {
                                vp = NULL;
-                               my_len = rad_attr2vp(NULL, NULL, NULL, NULL, attr, len, &vp);
+                               my_len = rad_attr2vp(NULL, &my_packet, &my_original, my_secret, attr, len, &vp);
                                if (my_len < 0) {
-                                       pairfree(&head);
+                                       fr_pair_list_free(&head);
                                        break;
                                }
 
@@ -751,7 +774,90 @@ static void process_file(const char *root_dir, char const *filename)
                                        }
                                }
 
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
+                       } else if (my_len < 0) {
+                               strlcpy(output, fr_strerror(), sizeof(output));
+
+                       } else { /* zero-length attribute */
+                               *output = '\0';
+                       }
+                       continue;
+               }
+
+               /*
+                *      And some DHCP tests
+                */
+               if (strncmp(p, "encode-dhcp ", 12) == 0) {
+                       vp_cursor_t cursor;
+
+                       if (strcmp(p + 12, "-") == 0) {
+                               p = output;
+                       } else {
+                               p += 12;
+                       }
+
+                       if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
+                               strlcpy(output, fr_strerror(), sizeof(output));
+                               continue;
+                       }
+
+                       fr_cursor_init(&cursor, &head);
+
+
+                       attr = data;
+                       vp = head;
+
+                       while ((vp = fr_cursor_current(&cursor))) {
+                               len = fr_dhcp_encode_option(NULL, attr, data + sizeof(data) - attr, &cursor);
+                               if (len < 0) {
+                                       fprintf(stderr, "Failed encoding %s: %s\n",
+                                               vp->da->name, fr_strerror());
+                                       exit(1);
+                               }
+                               attr += len;
+                       };
+
+                       fr_pair_list_free(&head);
+                       outlen = attr - data;
+                       goto print_hex;
+               }
+
+               if (strncmp(p, "decode-dhcp ", 12) == 0) {
+                       ssize_t my_len;
+
+                       if (strcmp(p + 12, "-") == 0) {
+                               attr = data;
+                               len = data_len;
+                       } else {
+                               attr = data;
+                               len = encode_hex(p + 12, data, sizeof(data));
+                               if (len == 0) {
+                                       fprintf(stderr, "Failed decoding hex string at line %d of %s\n", lineno, directory);
+                                       exit(1);
+                               }
+                       }
+
+                       my_len = fr_dhcp_decode_options(NULL, &head, attr, len);
+
+                       /*
+                        *      Output may be an error, and we ignore
+                        *      it if so.
+                        */
+                       if (head) {
+                               vp_cursor_t cursor;
+                               p = output;
+                               for (vp = fr_cursor_init(&cursor, &head);
+                                    vp;
+                                    vp = fr_cursor_next(&cursor)) {
+                                       vp_prints(p, sizeof(output) - (p - output), vp);
+                                       p += strlen(p);
+
+                                       if (vp->next) {strcpy(p, ", ");
+                                               p += 2;
+                                       }
+                               }
+
+                               fr_pair_list_free(&head);
                        } else if (my_len < 0) {
                                strlcpy(output, fr_strerror(), sizeof(output));
 
@@ -764,7 +870,7 @@ static void process_file(const char *root_dir, char const *filename)
                if (strncmp(p, "attribute ", 10) == 0) {
                        p += 10;
 
-                       if (userparse(NULL, p, &head) != T_EOL) {
+                       if (fr_pair_list_afrom_str(NULL, p, &head) != T_EOL) {
                                strlcpy(output, fr_strerror(), sizeof(output));
                                continue;
                        }
@@ -810,12 +916,24 @@ static void process_file(const char *root_dir, char const *filename)
        if (fp != stdin) fclose(fp);
 }
 
+static void NEVER_RETURNS usage(void)
+{
+       fprintf(stderr, "usage: radattr [OPTS] filename\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, "  -x                     Debugging mode.\n");
+       fprintf(stderr, "  -M                     Show talloc memory report.\n");
+
+       exit(1);
+}
+
 int main(int argc, char *argv[])
 {
        int c;
        bool report = false;
        char const *radius_dir = RADDBDIR;
        char const *dict_dir = DICTDIR;
+       int *inst = &c;
 
        cf_new_escape = true;   /* fix the tests */
 
@@ -826,7 +944,7 @@ int main(int argc, char *argv[])
        }
 #endif
 
-       while ((c = getopt(argc, argv, "d:D:xM")) != EOF) switch (c) {
+       while ((c = getopt(argc, argv, "d:D:xMh")) != EOF) switch (c) {
                case 'd':
                        radius_dir = optarg;
                        break;
@@ -834,15 +952,15 @@ int main(int argc, char *argv[])
                        dict_dir = optarg;
                        break;
                case 'x':
-                       fr_debug_flag++;
-                       debug_flag = fr_debug_flag;
+                       fr_debug_lvl++;
+                       rad_debug_lvl = fr_debug_lvl;
                        break;
                case 'M':
                        report = true;
                        break;
+               case 'h':
                default:
-                       fprintf(stderr, "usage: radattr [OPTS] filename\n");
-                       exit(1);
+                       usage();
        }
        argc -= (optind - 1);
        argv += (optind - 1);
@@ -865,7 +983,7 @@ int main(int argc, char *argv[])
                return 1;
        }
 
-       if (xlat_register("test", xlat_test, NULL, NULL) < 0) {
+       if (xlat_register("test", xlat_test, NULL, inst) < 0) {
                fprintf(stderr, "Failed registering xlat");
                return 1;
        }
index db880d2..7a8a034 100644 (file)
@@ -1,5 +1,5 @@
 TARGET         := radattr
 SOURCES                := radattr.c
 
-TGT_PREREQS    := libfreeradius-server.a libfreeradius-radius.a
+TGT_PREREQS    := libfreeradius-server.a libfreeradius-radius.a libfreeradius-dhcp.a
 TGT_LDLIBS     := $(LIBS)
index d90fab8..016e560 100644 (file)
@@ -161,24 +161,27 @@ static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
        VALUE_PAIR *challenge, *reply;
        uint8_t nthash[16];
 
-       challenge = paircreate(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT);
+       fr_pair_delete_by_num(&packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
+       fr_pair_delete_by_num(&packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
+
+       challenge = fr_pair_afrom_num(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT);
        if (!challenge) {
                return 0;
        }
 
-       pairadd(request, challenge);
+       fr_pair_add(request, challenge);
        challenge->vp_length = 8;
        challenge->vp_octets = p = talloc_array(challenge, uint8_t, challenge->vp_length);
        for (i = 0; i < challenge->vp_length; i++) {
                p[i] = fr_rand();
        }
 
-       reply = paircreate(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
+       reply = fr_pair_afrom_num(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
        if (!reply) {
                return 0;
        }
 
-       pairadd(request, reply);
+       fr_pair_add(request, reply);
        reply->vp_length = 50;
        reply->vp_octets = p = talloc_array(reply, uint8_t, reply->vp_length);
        memset(p, 0, reply->vp_length);
@@ -261,6 +264,30 @@ static PW_CODE radclient_get_code(uint16_t port)
        return PW_CODE_UNDEFINED;
 }
 
+
+static bool already_hex(VALUE_PAIR *vp)
+{
+       size_t i;
+
+       if (!vp || (vp->da->type != PW_TYPE_OCTETS)) return true;
+
+       /*
+        *      If it's 17 octets, it *might* be already encoded.
+        *      Or, it might just be a 17-character password (maybe UTF-8)
+        *      Check it for non-printable characters.  The odds of ALL
+        *      of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
+        *      or 1/(2^51), which is pretty much zero.
+        */
+       for (i = 0; i < vp->vp_length; i++) {
+               if (vp->vp_octets[i] < 32) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+
 /*
  *     Initialize a radclient data structure and add it to
  *     the global linked list.
@@ -337,7 +364,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                /*
                 *      Read the request VP's.
                 */
-               if (readvp2(request->packet, &request->packet->vps, packets, &packets_done) < 0) {
+               if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, packets, &packets_done) < 0) {
                        char const *input;
 
                        if ((files->packets[0] == '-') && (files->packets[1] == '\0')) {
@@ -364,7 +391,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                if (filters) {
                        bool filters_done;
 
-                       if (readvp2(request, &request->filter, filters, &filters_done) < 0) {
+                       if (fr_pair_list_afrom_file(request, &request->filter, filters, &filters_done) < 0) {
                                REDEBUG("Error parsing \"%s\"", files->filters);
                                goto error;
                        }
@@ -408,10 +435,9 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                        /*
                         *      This allows efficient list comparisons later
                         */
-                       pairsort(&request->filter, attrtagcmp);
+                       fr_pair_list_sort(&request->filter, fr_pair_cmp_by_da_tag);
                }
 
-               request->password[0] = '\0';
                /*
                 *      Process special attributes
                 */
@@ -511,9 +537,9 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                                vp->da = da;
 
                                /*
-                                *      Re-do pairmemsteal ourselves,
+                                *      Re-do fr_pair_value_memsteal ourselves,
                                 *      because we play games with
-                                *      vp->da, and pairmemsteal goes
+                                *      vp->da, and fr_pair_value_memsteal goes
                                 *      to GREAT lengths to sanitize
                                 *      and fix and change and
                                 *      double-check the various
@@ -529,13 +555,35 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
                        }
                                break;
 
+                               /*
+                                *      Cache this for later.
+                                */
+                       case PW_CLEARTEXT_PASSWORD:
+                               request->password = vp;
+                               break;
+
                        /*
                         *      Keep a copy of the the password attribute.
                         */
-                       case PW_USER_PASSWORD:
                        case PW_CHAP_PASSWORD:
+                               /*
+                                *      If it's already hex, do nothing.
+                                */
+                               if ((vp->vp_length == 17) &&
+                                   (already_hex(vp))) break;
+
+                               /*
+                                *      CHAP-Password is octets, so it may not be zero terminated.
+                                */
+                               request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password",
+                                                            "", T_OP_EQ);
+                               fr_pair_value_bstrncpy(request->password, vp->vp_strvalue, vp->vp_length);
+                               break;
+
+                       case PW_USER_PASSWORD:
                        case PW_MS_CHAP_PASSWORD:
-                               strlcpy(request->password, vp->vp_strvalue, sizeof(request->password));
+                               request->password = fr_pair_make(request->packet, &request->packet->vps, "Cleartext-Password",
+                                                            vp->vp_strvalue, T_OP_EQ);
                                break;
 
                        case PW_RADCLIENT_TEST_NAME:
@@ -807,9 +855,9 @@ static int send_one_packet(rc_request_t *request)
 
 #ifdef WITH_TCP
                        if (proto) {
-                               mysockfd = fr_tcp_client_socket(NULL,
+                               mysockfd = fr_socket_client_tcp(NULL,
                                                                &request->packet->dst_ipaddr,
-                                                               request->packet->dst_port);
+                                                               request->packet->dst_port, false);
                        } else
 #endif
                        mysockfd = fr_socket(&client_ipaddr, 0);
@@ -837,52 +885,21 @@ static int send_one_packet(rc_request_t *request)
                 *      Update the password, so it can be encrypted with the
                 *      new authentication vector.
                 */
-               if (request->password[0] != '\0') {
+               if (request->password) {
                        VALUE_PAIR *vp;
 
-                       if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
-                               pairstrcpy(vp, request->password);
-                       } 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.
-                                *      Or, it might just be a 17-character password (maybe UTF-8)
-                                *      Check it for non-printable characters.  The odds of ALL
-                                *      of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
-                                *      or 1/(2^51), which is pretty much zero.
-                                */
-                               if (vp->vp_length == 17) {
-                                       for (i = 0; i < 17; i++) {
-                                               if (vp->vp_octets[i] < 32) {
-                                                       already_hex = true;
-                                                       break;
-                                               }
-                                       }
-                               }
-
-                               /*
-                                *      Allow the user to specify ASCII or hex CHAP-Password
-                                */
-                               if (!already_hex) {
-                                       uint8_t *p;
-                                       size_t len, len2;
+                       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
+                               fr_pair_value_strcpy(vp, request->password->vp_strvalue);
 
-                                       len = len2 = strlen(request->password);
-                                       if (len2 < 17) len2 = 17;
+                       } else if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
+                               uint8_t buffer[17];
 
-                                       p = talloc_zero_array(vp, uint8_t, len2);
+                               rad_chap_encode(request->packet, buffer, fr_rand() & 0xff, request->password);
+                               fr_pair_value_memcpy(vp, buffer, 17);
 
-                                       memcpy(p, request->password, len);
+                       } else if (fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) {
+                               mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue);
 
-                                       rad_chap_encode(request->packet,
-                                                       p,
-                                                       fr_rand() & 0xff, vp);
-                                       vp->vp_octets = p;
-                                       vp->vp_length = 17;
-                               }
-                       } 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");
                        }
@@ -957,10 +974,13 @@ static int send_one_packet(rc_request_t *request)
         */
        if (rad_send(request->packet, NULL, secret) < 0) {
                REDEBUG("Failed to send packet for ID %d", request->packet->id);
+               deallocate_id(request);
+               request->done = true;
+               return -1;
        }
 
        fr_packet_header_print(fr_log_fp, request->packet, false);
-       if (fr_debug_flag > 0) vp_printlist(fr_log_fp, request->packet->vps);
+       if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps);
 
        return 0;
 }
@@ -1008,17 +1028,26 @@ static int recv_one_packet(int wait_time)
        }
 
        /*
-        *      udpfromto issues.  We may have bound to "*",
-        *      and we want to find the replies that are sent to
-        *      (say) 127.0.0.1.
+        *      We don't use udpfromto.  So if we bind to "*", we want
+        *      to find replies sent to 192.0.2.4.  Therefore, we
+        *      force all replies to have the one address we know
+        *      about, no matter what real address they were sent to.
         *
         *      This only works if were not using any of the
         *      Packet-* attributes, or running with 'auto'.
         */
        reply->dst_ipaddr = client_ipaddr;
        reply->dst_port = client_port;
+
 #ifdef WITH_TCP
-       if (server_port > 0) {
+
+       /*
+        *      TCP sockets don't use recvmsg(), and thus don't get
+        *      the source IP/port.  However, since they're TCP, we
+        *      know what the source IP/port is, because that's where
+        *      we connected to.
+        */
+       if (ipproto == IPPROTO_TCP) {
                reply->src_ipaddr = server_ipaddr;
                reply->src_port = server_port;
        }
@@ -1061,7 +1090,7 @@ static int recv_one_packet(int wait_time)
        }
 
        fr_packet_header_print(fr_log_fp, request->reply, true);
-       if (fr_debug_flag > 0) vp_printlist(fr_log_fp, request->reply->vps);
+       if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->reply->vps);
 
        /*
         *      Increment counters...
@@ -1102,12 +1131,12 @@ static int recv_one_packet(int wait_time)
        } else {
                VALUE_PAIR const *failed[2];
 
-               pairsort(&request->reply->vps, attrtagcmp);
-               if (pairvalidate(failed, request->filter, request->reply->vps)) {
+               fr_pair_list_sort(&request->reply->vps, fr_pair_cmp_by_da_tag);
+               if (fr_pair_validate(failed, request->filter, request->reply->vps)) {
                        RDEBUG("%s: Response passed filter", request->name);
                        stats.passed++;
                } else {
-                       pairvalidate_debug(request, failed);
+                       fr_pair_validate_debug(request, failed);
                        REDEBUG("%s: Response for failed filter", request->name);
                        stats.failed++;
                }
@@ -1126,23 +1155,23 @@ packet_done:
 
 int main(int argc, char **argv)
 {
-       int c;
-       char const *radius_dir = RADDBDIR;
-       char const *dict_dir = DICTDIR;
-       char filesecret[256];
-       FILE *fp;
-       int do_summary = false;
-       int persec = 0;
-       int parallel = 1;
+       int             c;
+       char            const *radius_dir = RADDBDIR;
+       char            const *dict_dir = DICTDIR;
+       char            filesecret[256];
+       FILE            *fp;
+       int             do_summary = false;
+       int             persec = 0;
+       int             parallel = 1;
        rc_request_t    *this;
-       int force_af = AF_UNSPEC;
+       int             force_af = AF_UNSPEC;
 
        /*
         *      It's easier having two sets of flags to set the
         *      verbosity of library calls and the verbosity of
         *      radclient.
         */
-       fr_debug_flag = 0;
+       fr_debug_lvl = 0;
        fr_log_fp = stdout;
 
 #ifndef NDEBUG
@@ -1307,11 +1336,12 @@ int main(int argc, char **argv)
                        break;
 
                case 'v':
+                       fr_debug_lvl = 1;
                        DEBUG("%s", radclient_version);
                        exit(0);
 
                case 'x':
-                       fr_debug_flag++;
+                       fr_debug_lvl++;
                        break;
 
                case 'h':
@@ -1360,46 +1390,13 @@ int main(int argc, char **argv)
        /*
         *      Resolve hostname.
         */
-       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];
-
-               if (*argv[1] == '[') { /* IPv6 URL encoded */
-                       p = strchr(argv[1], ']');
-                       if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
-                               usage();
-                       }
-
-                       memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
-                       buffer[p - argv[1] - 1] = '\0';
-
-                       hostname = buffer;
-                       portname = p + 1;
-
-               }
-               p = strchr(portname, ':');
-               if (p && (strchr(p + 1, ':') == NULL)) {
-                       *p = '\0';
-                       portname = p + 1;
-               } else {
-                       portname = NULL;
-               }
-
-               if (ip_hton(&server_ipaddr, force_af, hostname, false) < 0) {
-                       ERROR("Failed to find IP address for host %s: %s", hostname, strerror(errno));
+               if (fr_pton_port(&server_ipaddr, &server_port, argv[1], -1, force_af, true) < 0) {
+                       ERROR("%s", fr_strerror());
                        exit(1);
                }
 
                /*
-                *      Strip port from hostname if needed.
-                */
-               if (portname) server_port = atoi(portname);
-
-               /*
                 *      Work backwards from the port to determine the packet type
                 */
                if (packet_code == PW_CODE_UNDEFINED) packet_code = radclient_get_code(server_port);
@@ -1455,7 +1452,7 @@ int main(int argc, char **argv)
 
 #ifdef WITH_TCP
        if (proto) {
-               sockfd = fr_tcp_client_socket(NULL, &server_ipaddr, server_port);
+               sockfd = fr_socket_client_tcp(NULL, &server_ipaddr, server_port, false);
        } else
 #endif
        sockfd = fr_socket(&client_ipaddr, client_port);
@@ -1547,7 +1544,10 @@ int main(int argc, char **argv)
                                /*
                                 *      Send the current packet.
                                 */
-                               send_one_packet(this);
+                               if (send_one_packet(this) < 0) {
+                                       talloc_free(this);
+                                       break;
+                               }
 
                                /*
                                 *      Wait a little before sending
index 1a2df8d..99ee4b5 100644 (file)
@@ -54,13 +54,11 @@ RCSID("$Id$")
 /*
  *  Global variables.
  */
-char const *progname = NULL;
-char const *radacct_dir = NULL;
-char const *radlog_dir = NULL;
-char const *radlib_dir = NULL;
-bool log_stripped_names;
-log_lvl_t debug_flag = 0;
-bool check_config = false;
+char const     *progname = NULL;
+char const     *radacct_dir = NULL;
+char const     *radlog_dir = NULL;
+char const     *radlib_dir = NULL;
+bool           log_stripped_names;
 
 char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
 #ifdef RADIUSD_VERSION_COMMIT
@@ -93,10 +91,10 @@ int main(int argc, char *argv[])
        int status;
        int argval;
        bool spawn_flag = true;
-       bool write_pid = false;
        bool display_version = false;
        int flag = 0;
        int from_child[2] = {-1, -1};
+       fr_state_t *state = NULL;
 
        /*
         *  We probably don't want to free the talloc autofree context
@@ -124,7 +122,7 @@ int main(int argc, char *argv[])
        }
 #endif
 
-       debug_flag = 0;
+       rad_debug_lvl = 0;
        set_radius_dir(autofree, RADIUS_DIR);
 
        /*
@@ -159,7 +157,7 @@ int main(int argc, char *argv[])
                                break;
 
                        case 'D':
-                               main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
+                               main_config.dictionary_dir = talloc_typed_strdup(autofree, optarg);
                                break;
 
                        case 'f':
@@ -223,7 +221,7 @@ int main(int argc, char *argv[])
 
                        case 'P':
                                /* Force the PID to be written, even in -f mode */
-                               write_pid = true;
+                               main_config.write_pid = true;
                                break;
 
                        case 's':       /* Single process mode */
@@ -242,7 +240,7 @@ int main(int argc, char *argv[])
                        case 'X':
                                spawn_flag = false;
                                main_config.daemonize = false;
-                               debug_flag += 2;
+                               rad_debug_lvl += 2;
                                main_config.log_auth = true;
                                main_config.log_auth_badpass = true;
                                main_config.log_auth_goodpass = true;
@@ -253,7 +251,7 @@ int main(int argc, char *argv[])
                                break;
 
                        case 'x':
-                               debug_flag++;
+                               rad_debug_lvl++;
                                break;
 
                        default:
@@ -306,7 +304,7 @@ int main(int argc, char *argv[])
         */
        if (display_version) {
                /* Don't print timestamps */
-               debug_flag += 2;
+               rad_debug_lvl += 2;
                fr_log_fp = stdout;
                default_log.dst = L_DST_STDOUT;
                default_log.fd = STDOUT_FILENO;
@@ -316,7 +314,7 @@ int main(int argc, char *argv[])
                exit(EXIT_SUCCESS);
        }
 
-       if (debug_flag) version_print();
+       if (rad_debug_lvl) version_print();
 
        /*
         *  Under linux CAP_SYS_PTRACE is usually only available before setuid/setguid,
@@ -333,6 +331,11 @@ int main(int argc, char *argv[])
 #endif
 
        /*
+        *  Write the PID always if we're running as a daemon.
+        */
+       if (main_config.daemonize) main_config.write_pid = true;
+
+       /*
         *  Read the configuration files, BEFORE doing anything else.
         */
        if (main_config_init() < 0) exit(EXIT_FAILURE);
@@ -349,16 +352,21 @@ int main(int argc, char *argv[])
        if (tls_global_version_check(main_config.allow_vulnerable_openssl) < 0) exit(EXIT_FAILURE);
 #endif
 
+       fr_talloc_fault_setup();
+
        /*
         *  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);
+       {
+               char const *panic_action = NULL;
+
+               panic_action = getenv("PANIC_ACTION");
+               if (!panic_action) panic_action = main_config.panic_action;
+
+               if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) {
+                       fr_perror("radiusd");
+                       exit(EXIT_FAILURE);
+               }
        }
 
 #ifndef __MINGW32__
@@ -492,7 +500,7 @@ int main(int argc, char *argv[])
         *  immediately.  Use SIGTERM to shut down the server cleanly in
         *  that case.
         */
-       if (main_config.debug_memory || (debug_flag == 0)) {
+       if (main_config.debug_memory || (rad_debug_lvl == 0)) {
                if ((fr_set_signal(SIGINT, sig_fatal) < 0)
 #ifdef SIGQUIT
                || (fr_set_signal(SIGQUIT, sig_fatal) < 0)
@@ -520,14 +528,9 @@ int main(int argc, char *argv[])
 #endif
 
        /*
-        *  Write the PID always if we're running as a daemon.
-        */
-       if (main_config.daemonize) write_pid = true;
-
-       /*
         *  Write the PID after we've forked, so that we write the correct one.
         */
-       if (write_pid) {
+       if (main_config.write_pid) {
                FILE *fp;
 
                fp = fopen(main_config.pid_file, "w");
@@ -568,7 +571,7 @@ int main(int argc, char *argv[])
        /*
         *  Initialise the state rbtree (used to link multiple rounds of challenges).
         */
-       fr_state_init();
+       state = fr_state_init(NULL);
 
        /*
         *  Process requests until HUP or exit.
@@ -622,7 +625,7 @@ cleanup:
 
        xlat_free();            /* modules may have xlat's */
 
-       fr_state_delete();
+       fr_state_delete(state);
 
        /*
         *  Free the configuration items.
@@ -667,15 +670,15 @@ static void NEVER_RETURNS usage(int status)
        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, "  -m            On SIGINT or SIGQUIT exit cleanly instead of immediately.\n");
+       fprintf(output, "  -m            On SIGINT or SIGQUIT clean up all used memory instead of just exiting.\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, "  -s            Do not spawn child processes to handle requests (same as -ft).\n");
        fprintf(output, "  -t            Disable threads.\n");
        fprintf(output, "  -v            Print server version information.\n");
-       fprintf(output, "  -X            Turn on full debugging.\n");
-       fprintf(output, "  -x            Turn on additional debugging. (-xx gives more debugging).\n");
+       fprintf(output, "  -X            Turn on full debugging (similar to -tfxxl stdout).\n");
+       fprintf(output, "  -x            Turn on additional debugging (-xx gives more debugging).\n");
        exit(status);
 }
 
index 0c4b3c9..5834078 100644 (file)
@@ -30,13 +30,6 @@ RCSID("$Id$")
 #include <pwd.h>
 #include <grp.h>
 
-#ifdef HAVE_SYS_UN_H
-#  include <sys/un.h>
-#  ifndef SUN_LEN
-#    define SUN_LEN(su)  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
-#  endif
-#endif
-
 #ifdef HAVE_GETOPT_H
 #  include <getopt.h>
 #endif
@@ -84,10 +77,7 @@ static char const *radmin_version = "radmin version " RADIUSD_VERSION_STRING
  *     they're running inside of the server.  And we don't (yet)
  *     have a "libfreeradius-server", or "libfreeradius-util".
  */
-log_lvl_t debug_flag = 0;
-struct main_config_t main_config;
-
-bool check_config = false;
+main_config_t main_config;
 
 static bool echo = false;
 static char const *secret = "testing123";
@@ -122,53 +112,6 @@ static void NEVER_RETURNS usage(int status)
        exit(status);
 }
 
-static int fr_domain_socket(char const *path)
-{
-       int sockfd = -1;
-#ifdef HAVE_SYS_UN_H
-       size_t len;
-       socklen_t socklen;
-       struct sockaddr_un saremote;
-
-       len = strlen(path);
-       if (len >= sizeof(saremote.sun_path)) {
-               fprintf(stderr, "%s: Path too long in filename\n", progname);
-               return -1;
-       }
-
-       if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
-               fprintf(stderr, "%s: Failed creating socket: %s\n",
-                       progname, fr_syserror(errno));
-               return -1;
-       }
-
-       saremote.sun_family = AF_UNIX;
-       memcpy(saremote.sun_path, path, len + 1); /* SUN_LEN does strlen */
-
-       socklen = SUN_LEN(&saremote);
-
-       if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
-               struct stat buf;
-
-               close(sockfd);
-               fprintf(stderr, "%s: Failed connecting to %s: %s\n",
-                       progname, path, fr_syserror(errno));
-
-               /*
-                *      The file doesn't exist.  Tell the user how to
-                *      fix it.
-                */
-               if ((stat(path, &buf) < 0) &&
-                   (errno == ENOENT)) {
-                       fprintf(stderr, "  Perhaps you need to run the commands:\n\tcd /etc/raddb\n\tln -s sites-available/control-socket sites-enabled/control-socket\n  and then re-start the server?\n");
-               }
-
-               return -1;
-       }
-#endif
-       return sockfd;
-}
-
 static int client_socket(char const *server)
 {
        int sockfd;
@@ -192,7 +135,7 @@ static int client_socket(char const *server)
                exit(1);
        }
 
-       sockfd = fr_tcp_client_socket(NULL, &ipaddr, port);
+       sockfd = fr_socket_client_tcp(NULL, &ipaddr, port, false);
        if (sockfd < 0) {
                fprintf(stderr, "%s: Failed opening socket %s: %s\n",
                        progname, server, fr_syserror(errno));
@@ -277,8 +220,7 @@ static ssize_t run_command(int sockfd, char const *command,
 
                        memcpy(&status, buffer, sizeof(status));
                        status = ntohl(status);
-                       // set the status from the data
-                       return 1 + status;
+                       return status;
 
                default:
                        fprintf(stderr, "Unexpected response\n");
@@ -310,8 +252,18 @@ static int do_connect(int *out, char const *file, char const *server)
                /*
                 *      FIXME: Get destination from command line, if possible?
                 */
-               sockfd = fr_domain_socket(file);
-               if (sockfd < 0) return -1;
+               sockfd = fr_socket_client_unix(file, false);
+               if (sockfd < 0) {
+                       fr_perror("radmin");
+                       if (errno == ENOENT) {
+                                       fprintf(stderr, "Perhaps you need to run the commands:");
+                                       fprintf(stderr, "\tcd /etc/raddb\n");
+                                       fprintf(stderr, "\tln -s sites-available/control-socket "
+                                               "sites-enabled/control-socket\n");
+                                       fprintf(stderr, "and then re-start the server?\n");
+                       }
+                       return -1;
+               }
        } else {
                sockfd = client_socket(server);
        }
@@ -517,6 +469,7 @@ int main(int argc, char **argv)
 
                if (cf_file_read(cs, buffer) < 0) {
                        fprintf(stderr, "%s: Errors reading or parsing %s\n", progname, buffer);
+                       talloc_free(cs);
                        usage(1);
                }
 
@@ -643,7 +596,7 @@ int main(int argc, char **argv)
                        len = run_command(sockfd, commands[i], buffer, sizeof(buffer));
                        if (len < 0) exit(1);
 
-                       if (len == 1) exit_status = EXIT_FAILURE;
+                       if (len == FR_CHANNEL_FAIL) exit_status = EXIT_FAILURE;
                }
                exit(exit_status);
        }
@@ -662,6 +615,8 @@ int main(int argc, char **argv)
         */
 
        while (1) {
+               int retries;
+
 #ifndef USE_READLINE
                if (!quiet) {
                        printf("radmin> ");
@@ -760,14 +715,26 @@ int main(int argc, char **argv)
                        continue;
                }
 
+               retries = 0;
+       retry:
                len = run_command(sockfd, line, buffer, sizeof(buffer));
-               if ((len < 0) && (do_connect(&sockfd, file, server) < 0)) {
-                       fprintf(stderr, "Reconnecting...");
+               if (len < 0) {
+                       if (!quiet) fprintf(stderr, "... reconnecting ...\n");
+
+                       if (do_connect(&sockfd, file, server) < 0) {
+                               exit(1);
+                       }
+
+                       retries++;
+                       if (retries < 2) goto retry;
+
+                       fprintf(stderr, "Failed to connect to server\n");
                        exit(1);
 
-               } else if (len == 0) {
+               } else if (len == FR_CHANNEL_SUCCESS) {
                        break;
-               } else if (len == 1) {
+
+               } else if (len == FR_CHANNEL_FAIL) {
                        exit_status = EXIT_FAILURE;
                }
        }
index 054bcb6..375fe26 100644 (file)
@@ -63,9 +63,9 @@ static char const *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING
 ", built on " __DATE__ " at " __TIME__;
 
 static int rs_useful_codes[] = {
-       PW_CODE_ACCESS_REQUEST,         //!< RFC2865 - Authentication request
-       PW_CODE_ACCESS_ACCEPT,          //!< RFC2865 - Access-Accept
-       PW_CODE_ACCESS_REJECT,          //!< RFC2865 - Access-Reject
+       PW_CODE_ACCESS_REQUEST,                 //!< RFC2865 - Authentication request
+       PW_CODE_ACCESS_ACCEPT,                  //!< RFC2865 - Access-Accept
+       PW_CODE_ACCESS_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
@@ -216,13 +216,6 @@ static void rs_time_print(char *out, size_t len, struct timeval const *t)
        }
 }
 
-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;
@@ -368,7 +361,7 @@ static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *h
                VALUE_PAIR *vp;
 
                for (i = 0; i < conf->list_da_num; i++) {
-                       vp = pair_find_by_da(packet->vps, conf->list_da[i], TAG_ANY);
+                       vp = fr_pair_find_by_da(packet->vps, conf->list_da[i], TAG_ANY);
                        if (vp && (vp->vp_length > 0)) {
                                if (conf->list_da[i]->type == PW_TYPE_STRING) {
                                        *p++ = '"';
@@ -483,15 +476,15 @@ static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t
                /*
                 *      Print out verbose HEX output
                 */
-               if (conf->print_packet && (fr_debug_flag > 3)) {
+               if (conf->print_packet && (fr_debug_lvl > 3)) {
                        rad_print_hex(packet);
                }
 
-               if (conf->print_packet && (fr_debug_flag > 1)) {
+               if (conf->print_packet && (fr_debug_lvl > 1)) {
                        char vector[(AUTH_VECTOR_LEN * 2) + 1];
 
                        if (packet->vps) {
-                               pairsort(&packet->vps, attrtagcmp);
+                               fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da_tag);
                                vp_printlist(fr_log_fp, packet->vps);
                        }
 
@@ -501,6 +494,16 @@ static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t
        }
 }
 
+static inline void rs_packet_print(rs_request_t *request, uint64_t count, rs_status_t status, fr_pcap_t *handle,
+                                  RADIUS_PACKET *packet, struct timeval *elapsed, struct timeval *latency,
+                                  bool response, bool body)
+{
+       if (!conf->logger) return;
+
+       if (request) request->logged = true;
+       conf->logger(count, status, handle, packet, elapsed, latency, response, body);
+}
+
 static void rs_stats_print(rs_latency_t *stats, PW_CODE code)
 {
        int i;
@@ -699,7 +702,7 @@ static void rs_stats_process(void *ctx)
        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) {
+               if (fr_debug_lvl > 0) {
                        rs_stats_print(&stats->exchange[rs_useful_codes[i]], rs_useful_codes[i]);
                }
        }
@@ -779,9 +782,9 @@ static int rs_get_pairs(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR *vps, DICT
                }
 
                do {
-                       copy = paircopyvp(ctx, match);
+                       copy = fr_pair_copy(ctx, match);
                        if (!copy) {
-                               pairfree(out);
+                               fr_pair_list_free(out);
                                return -1;
                        }
                        fr_cursor_insert(&out_cursor, copy);
@@ -854,18 +857,19 @@ static void rs_packet_cleanup(rs_request_t *request)
         */
        if (!request->silent_cleanup) {
                if (!request->linked) {
-                       RS_ASSERT(request && request->stats_req);
+                       if (!request->stats_req) return;
+
                        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));
+                               rs_packet_print(request, 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) {
+               if ((request->in->type == PCAP_INTERFACE_IN) && request->logged) {
                        RDEBUG("Cleaning up request packet ID %i", request->expect->id);
                }
        }
@@ -914,7 +918,7 @@ static inline int rs_response_to_pcap(rs_event_t *event, rs_request_t *request,
         *      If we're filtering by response then the requests then the capture buffer
         *      associated with the request should contain buffered request packets.
         */
-       if (conf->filter_response) {
+       if (conf->filter_response && request) {
                rs_capture_t *start;
 
                /*
@@ -1038,7 +1042,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                rs_time_print(timestr, sizeof(timestr), &header->ts);
        }
 
-       len = fr_link_layer_offset(data, header->caplen, event->in->link_type);
+       len = fr_link_layer_offset(data, header->caplen, event->in->link_layer);
        if (len < 0) {
                REDEBUG("Failed determining link layer header offset");
                return;
@@ -1151,7 +1155,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
        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);
+                       rs_packet_print(NULL, count, RS_ERROR, event->in, current, &elapsed, NULL, false, false);
                }
                rad_free(&current);
 
@@ -1214,8 +1218,8 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                         *      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)) {
+                               fr_pair_list_sort(&current->vps, fr_pair_cmp_by_da_tag);
+                               if (!fr_pair_validate_relaxed(NULL, conf->filter_response_vps, current->vps)) {
                                        goto drop_response;
                                }
                        }
@@ -1313,7 +1317,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                                return;
                        }
 
-                       pairsort(&current->vps, attrtagcmp);
+                       fr_pair_list_sort(&current->vps, fr_pair_cmp_by_da_tag);
                }
 
                /*
@@ -1385,7 +1389,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                 *      Now verify the packet passes the attribute filter
                 */
                if (conf->filter_request_vps) {
-                       if (!pairvalidate_relaxed(NULL, conf->filter_request_vps, current->vps)) {
+                       if (!fr_pair_validate_relaxed(NULL, conf->filter_request_vps, current->vps)) {
                                goto drop_request;
                        }
                }
@@ -1439,7 +1443,7 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                                for (vp = fr_cursor_init(&cursor, &search.link_vps);
                                     vp;
                                     vp = fr_cursor_next(&cursor)) {
-                                       pairsteal(original, search.link_vps);
+                                       fr_pair_steal(original, search.link_vps);
                                }
                                original->link_vps = search.link_vps;
 
@@ -1524,13 +1528,15 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
                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_packet_print(original, 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);
+                       rs_packet_print(original, count, status, event->in, current,
+                                       &elapsed, &latency, response, true);
                }
        /*
         *      It's the original request
@@ -1538,8 +1544,8 @@ static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkt
         *      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);
+               rs_packet_print(original, original ? original->id : count, status, event->in,
+                               current, &elapsed, NULL, response, true);
        }
 
        fflush(fr_log_fp);
@@ -1664,7 +1670,7 @@ static int rs_rtx_cmp(rs_request_t const *a, rs_request_t const *b)
        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);
+       return fr_pair_list_cmp(a->link_vps, b->link_vps);
 }
 
 static int rs_build_dict_list(DICT_ATTR const **out, size_t len, char *list)
@@ -1708,7 +1714,7 @@ static int rs_build_filter(VALUE_PAIR **out, char const *filter)
        VALUE_PAIR *vp;
        FR_TOKEN code;
 
-       code = userparse(conf, filter, out);
+       code = fr_pair_list_afrom_str(conf, filter, out);
        if (code == T_INVALID) {
                ERROR("Invalid RADIUS filter \"%s\" (%s)", filter, fr_strerror());
                return -1;
@@ -1723,7 +1729,7 @@ static int rs_build_filter(VALUE_PAIR **out, char const *filter)
             vp;
             vp = fr_cursor_next(&cursor)) {
                /*
-                *      xlat expansion isn't support here
+                *      xlat expansion isn't supported here
                 */
                if (vp->type == VT_XLAT) {
                        vp->type = VT_DATA;
@@ -1735,7 +1741,7 @@ static int rs_build_filter(VALUE_PAIR **out, char const *filter)
        /*
         *      This allows efficient list comparisons later
         */
-       pairsort(out, attrtagcmp);
+       fr_pair_list_sort(out, fr_pair_cmp_by_da_tag);
 
        return 0;
 }
@@ -1933,7 +1939,7 @@ int main(int argc, char *argv[])
 
        rs_stats_t stats;
 
-       fr_debug_flag = 1;
+       fr_debug_lvl = 1;
        fr_log_fp = stdout;
 
        /*
@@ -1971,7 +1977,7 @@ int main(int argc, char *argv[])
        conf->stats.prefix = RS_DEFAULT_PREFIX;
 #endif
        conf->radius_secret = RS_DEFAULT_SECRET;
-       conf->logger = rs_packet_print_null;
+       conf->logger = NULL;
 
 #ifdef HAVE_COLLECTDC_H
        conf->stats.prefix = RS_DEFAULT_PREFIX;
@@ -2047,9 +2053,7 @@ int main(int argc, char *argv[])
 
                case 'i':
                        *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN);
-                       if (!*in_head) {
-                               goto finish;
-                       }
+                       if (!*in_head) goto finish;
                        in_head = &(*in_head)->next;
                        conf->from_dev = true;
                        break;
@@ -2085,8 +2089,8 @@ int main(int argc, char *argv[])
                        break;
 
                case 'q':
-                       if (fr_debug_flag > 0) {
-                               fr_debug_flag--;
+                       if (fr_debug_lvl > 0) {
+                               fr_debug_lvl--;
                        }
                        break;
 
@@ -2126,7 +2130,7 @@ int main(int argc, char *argv[])
 
                case 'x':
                case 'X':
-                       fr_debug_flag++;
+                       fr_debug_lvl++;
                        break;
 
                case 'W':
@@ -2238,7 +2242,7 @@ int main(int argc, char *argv[])
 
        if (conf->list_attributes) {
                conf->logger = rs_packet_print_csv;
-       } else if (fr_debug_flag > 0) {
+       } else if (fr_debug_lvl > 0) {
                conf->logger = rs_packet_print_fancy;
        }
 
@@ -2376,8 +2380,23 @@ int main(int argc, char *argv[])
                for (dev_p = all_devices;
                     dev_p;
                     dev_p = dev_p->next) {
+                       int link_layer;
+
                        /* Don't use the any devices, it's horribly broken */
                        if (!strcmp(dev_p->name, "any")) continue;
+
+                       link_layer = fr_pcap_if_link_layer(errbuf, dev_p);
+                       if (link_layer < 0) {
+                               DEBUG2("Skipping %s: %s", dev_p->name, errbuf);
+                               continue;
+                       }
+
+                       if (!fr_link_layer_supported(link_layer)) {
+                               DEBUG2("Skipping %s: datalink type %s not supported",
+                                      dev_p->name, pcap_datalink_val_to_name(link_layer));
+                               continue;
+                       }
+
                        *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN);
                        in_head = &(*in_head)->next;
                }
@@ -2389,7 +2408,7 @@ int main(int argc, char *argv[])
        /*
         *      Print captures values which will be used
         */
-       if (fr_debug_flag > 2) {
+       if (fr_debug_lvl > 2) {
                DEBUG2("Sniffing with options:");
                if (conf->from_dev)     {
                        char *buff = fr_pcap_device_names(conf, in, ' ');
@@ -2472,6 +2491,12 @@ int main(int argc, char *argv[])
                                goto finish;
                        }
 
+                       if (!fr_link_layer_supported(in_p->link_layer)) {
+                               ERROR("Failed opening pcap handle (%s): Datalink type %s not supported",
+                                     in_p->name, pcap_datalink_val_to_name(in_p->link_layer));
+                               goto finish;
+                       }
+
                        if (conf->pcap_filter) {
                                if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) {
                                        ERROR("Failed applying filter");
@@ -2498,24 +2523,24 @@ int main(int argc, char *argv[])
         *      Open our output interface (if we have one);
         */
        if (out) {
-               out->link_type = -1;    /* Infer output link type from input */
+               out->link_layer = -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;
+                       if (out->link_layer < 0) {
+                               out->link_layer = in_p->link_layer;
                                continue;
                        }
 
-                       if (out->link_type != in_p->link_type) {
+                       if (out->link_layer != in_p->link_layer) {
                                ERROR("Asked to write to output file, but inputs do not have the same link type");
                                ret = 64;
                                goto finish;
                        }
                }
 
-               RS_ASSERT(out->link_type >= 0);
+               RS_ASSERT(out->link_layer >= 0);
 
                if (fr_pcap_open(out) < 0) {
                        ERROR("Failed opening pcap output (%s): %s", out->name, fr_strerror());
index 51b4657..29c1a34 100644 (file)
@@ -45,12 +45,12 @@ static char const *eol = "\n";
 static int showname = -1;
 static int showptype = 0;
 static int showcid = 0;
-log_lvl_t debug_flag = 0;
 char const *progname = "radwho";
 char const *radlog_dir = NULL;
 
 static char const *radutmp_file = NULL;
-static char const *raddb_dir = NULL;
+static char const *raddb_dir = RADDBDIR;
+static char const *dict_dir = DICTDIR;
 
 char const *radacct_dir = NULL;
 char const *radlib_dir = NULL;
@@ -60,7 +60,7 @@ bool log_stripped_names;
 /*
  *     Global, for log.c to use.
  */
-struct main_config_t main_config;
+main_config_t main_config;
 
 #include <sys/wait.h>
 pid_t rad_fork(void)
@@ -80,8 +80,8 @@ static struct radutmp_config_t {
 } radutmpconfig;
 
 static const CONF_PARSER module_config[] = {
-  { "filename", FR_CONF_POINTER(PW_TYPE_FILE_INPUT, &radutmpconfig.radutmp_fn), RADUTMP },
-  { NULL, -1, 0, NULL, NULL }
+       { "filename", FR_CONF_POINTER(PW_TYPE_FILE_INPUT, &radutmpconfig.radutmp_fn), RADUTMP },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -89,7 +89,7 @@ static const CONF_PARSER module_config[] = {
  */
 static char *fullname(char *username)
 {
-#ifdef HAVE_PWD_Hx
+#ifdef HAVE_PWD_H
        struct passwd *pwd;
        char *s;
 
@@ -225,10 +225,13 @@ int main(int argc, char **argv)
 
        talloc_set_log_stderr();
 
-       while((c = getopt(argc, argv, "d:fF:nN:sSipP:crRu:U:Z")) != EOF) switch (c) {
+       while((c = getopt(argc, argv, "d:D:fF:nN:sSipP:crRu:U:Z")) != EOF) switch (c) {
                case 'd':
                        raddb_dir = optarg;
                        break;
+               case 'D':
+                       dict_dir = optarg;
+                       break;
                case 'F':
                        radutmp_file = optarg;
                        break;
@@ -293,6 +296,17 @@ int main(int argc, char **argv)
                return 1;
        }
 
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("radwho");
+               return 1;
+       }
+
+       if (dict_read(raddb_dir, RADIUS_DICTIONARY) == -1) {
+               fr_perror("radwho");
+               return 1;
+       }
+       fr_strerror();  /* Clear the error buffer */
+
        /*
         *      Be safe.
         */
@@ -328,6 +342,7 @@ int main(int argc, char **argv)
        snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", raddb_dir);
        if (cf_file_read(maincs, buffer) < 0) {
                fprintf(stderr, "%s: Error reading or parsing radiusd.conf\n", argv[0]);
+               talloc_free(maincs);
                exit(1);
        }
 
index a06bb5c..f05f253 100755 (executable)
@@ -7,6 +7,7 @@ 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 "       -D dict_directory: directory where the dictionaries are 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)."
@@ -20,7 +21,9 @@ do
   case $1 in
       -h) usage;;
 
-      -d) RADDB="-d $2";shift;shift;;
+      -d) OPTS="$OPTS -d $2";shift;shift;;
+
+      -D) OPTS="$OPTS -D $2";shift;shift;;
 
       -N) NAS_IP_ADDR="-N $2";shift;shift;;
 
@@ -48,4 +51,4 @@ SECRET=$2
 #
 #  Radzap is now a wrapper around radwho & radclient.
 #
-radwho -ZR $RADDB $NAS_IP_ADDR $NAS_PORT $USER_NAME | radclient $DEBUG $RADDB -f - $SERVER acct $SECRET
+radwho -ZR $OPTS $NAS_IP_ADDR $NAS_PORT $USER_NAME | radclient $DEBUG $OPTS -f - $SERVER acct $SECRET
index 64e6bca..3ed4686 100644 (file)
@@ -110,8 +110,7 @@ static const CONF_PARSER proxy_config[] = {
        { "dead_time", FR_CONF_OFFSET(PW_TYPE_INTEGER, realm_config_t, dead_time), STRINGIFY(DEAD_TIME)  },
 
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 #endif
 
@@ -290,8 +289,7 @@ static CONF_PARSER limit_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 #ifdef WITH_COA
@@ -300,8 +298,7 @@ static CONF_PARSER home_server_coa[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 #endif
 
@@ -354,7 +351,7 @@ static CONF_PARSER home_server_config[] = {
        { "coa", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) home_server_coa },
 #endif
 
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -506,7 +503,7 @@ bool realm_home_server_add(home_server_t *home)
                inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr, buffer, sizeof(buffer));
 
                cf_log_err_cs(home->cs, "Duplicate home server address%s%s%s: %s:%s%s/%i",
-                             home->name ? " (already in use by" : "",
+                             home->name ? " (already in use by " : "",
                              home->name ? home->name : "",
                              home->name ? ")" : "",
                              buffer,
@@ -523,7 +520,16 @@ bool realm_home_server_add(home_server_t *home)
 
        if (!home_server_insert(home, home->cs)) return false;
 
-       if (home->dual) {
+       /*
+        *      Dual home servers cause us to auto-create an
+        *      accounting server for UDP sockets, and leave
+        *      everything alone for TLS sockets.
+        */
+       if (home->dual
+#ifdef WITH_TLS
+           && !home->tls
+#endif
+) {
                home_server_t *home2 = talloc(talloc_parent(home), home_server_t);
 
                memcpy(home2, home, sizeof(*home2));
@@ -531,6 +537,7 @@ bool realm_home_server_add(home_server_t *home)
                home2->type = HOME_TYPE_ACCT;
                home2->dual = true;
                home2->port++;
+
                home2->ping_user_password = NULL;
                home2->cs = home->cs;
                home2->parent_server = home->parent_server;
@@ -559,16 +566,16 @@ bool realm_home_server_add(home_server_t *home)
 home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SECTION *cs)
 {
        home_server_t   *home;
-
        CONF_SECTION    *tls;
 
        if (!rc) rc = realm_config; /* Use the global config */
 
        home = talloc_zero(ctx, home_server_t);
        home->name = cf_section_name2(cs);
-       home->log_name = home->name;
+       home->log_name = talloc_typed_strdup(home, home->name);
        home->cs = cs;
        home->state = HOME_STATE_UNKNOWN;
+       home->proto = IPPROTO_UDP;
 
        /*
         *      Parse the configuration into the home server
@@ -615,7 +622,7 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
                }
 
                home->secret = "";
-               home->log_name = home->server;
+               home->log_name = talloc_typed_strdup(home, home->server);
        /*
         *      Otherwise it's an invalid config section and we
         *      raise an error.
@@ -633,15 +640,15 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
 
                if (home->type_str) type = fr_str2int(home_server_types, home->type_str, HOME_TYPE_INVALID);
 
+               home->type = type;
+
                switch (type) {
                case HOME_TYPE_AUTH_ACCT:
                        home->dual = true;
-                       home->type = HOME_TYPE_AUTH;
                        break;
 
                case HOME_TYPE_AUTH:
                case HOME_TYPE_ACCT:
-                       home->type = type;
                        break;
 
 #ifdef WITH_COA
@@ -650,7 +657,6 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
                                cf_log_err_cs(cs, "Home servers of type \"coa\" cannot point to a virtual server");
                                goto error;
                        }
-                       home->type = type;
                        break;
 #endif
 
@@ -694,11 +700,11 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
        }
 
        {
-               int type = IPPROTO_UDP;
+               int proto = IPPROTO_UDP;
 
-               if (home->proto_str) type = fr_str2int(home_proto, home->proto_str, -1);
+               if (home->proto_str) proto = fr_str2int(home_proto, home->proto_str, -1);
 
-               switch (type) {
+               switch (proto) {
                case IPPROTO_UDP:
                        home_servers_udp = true;
                        break;
@@ -720,7 +726,7 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
                        goto error;
                }
 
-               home->proto = type;
+               home->proto = proto;
        }
 
        if (!home->server && rbtree_finddata(home_servers_byaddr, home)) {
@@ -732,6 +738,12 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
         *      Check the TLS configuration.
         */
        tls = cf_section_sub_find(cs, "tls");
+#ifndef WITH_TLS
+       if (tls) {
+               cf_log_err_cs(cs, "TLS transport is not available in this executable");
+               goto error;
+       }
+#endif
 
        /*
         *      If were doing RADSEC (tls+tcp) the secret should default
@@ -750,27 +762,49 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
        }
 
        /*
-        *      If the home is not a virtual server, guess the port
-        *      and look up the source ip address.
+        *      Virtual servers have some TLS restrictions.
         */
-       if (!home->server) {
+       if (home->server) {
+               if (tls) {
+                       cf_log_err_cs(cs, "Virtual home_servers cannot have a \"tls\" subsection");
+                       goto error;
+               }
+       } else {
+               /*
+                *      If the home is not a virtual server, guess the port
+                *      and look up the source ip address.
+                */
                rad_assert(home->ipaddr.af != AF_UNSPEC);
 
+#ifdef WITH_TLS
+               if (tls && (home->proto != IPPROTO_TCP)) {
+                       cf_log_err_cs(cs, "TLS transport is not available for UDP sockets");
+                       goto error;
+               }
+#endif
+
                /*
-                *      Guess the port if we need to.
+                *      Set the default port if necessary.
                 */
                if (home->port == 0) {
+                       char buffer[INET6_ADDRSTRLEN + 3];
+
                        /*
                         *      For RADSEC we use the special RADIUS over TCP/TLS port
                         *      for both accounting and authentication, but for some
                         *      bizarre reason for RADIUS over plain TCP we use separate
                         *      ports 1812 and 1813.
-                        *
-                        *      Blame whoever wrote RFC 6613.
                         */
+#ifdef WITH_TLS
                        if (tls) {
                                home->port = PW_RADIUS_TLS_PORT;
-                       } else switch (home->type) {
+                       } else
+#endif
+                       switch (home->type) {
+                       default:
+                               rad_assert(0);
+                               /* FALL-THROUGH */
+
                        case HOME_TYPE_AUTH:
                                home->port = PW_AUTH_UDP_PORT;
                                break;
@@ -782,10 +816,16 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
                        case HOME_TYPE_COA:
                                home->port = PW_COA_UDP_PORT;
                                break;
-
-                       default:
-                               rad_assert(0);
                        }
+
+                       /*
+                        *      Now that we have a real port, use that.
+                        */
+                       rad_const_free(home->log_name);
+
+                       fr_ntop(buffer, sizeof(buffer), &home->ipaddr);
+
+                       home->log_name = talloc_asprintf(home, "%s:%i", buffer, home->port);
                }
 
                /*
@@ -807,17 +847,7 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
                        home->src_ipaddr.af = home->ipaddr.af;
                }
 
-               if (tls && (home->proto != IPPROTO_TCP)) {
-                       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");
-                       goto error;
-               }
-#else
+#ifdef WITH_TLS
                /*
                 *      Parse the SSL client configuration.
                 */
@@ -828,11 +858,7 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
                        }
                }
 #endif
-
-       } else if (tls) {
-               cf_log_err_cs(cs, "Virtual home_servers cannot have a \"tls\" subsection");
-               goto error;
-       }
+       } /* end of parse home server */
 
        realm_home_server_sanitize(home, cs);
 
@@ -953,6 +979,21 @@ static int pool_check_home_server(UNUSED realm_config_t *rc, CONF_PAIR *cp,
                return 1;
        }
 
+       switch (server_type) {
+       case HOME_TYPE_AUTH:
+       case HOME_TYPE_ACCT:
+               myhome.type = HOME_TYPE_AUTH_ACCT;
+               home = rbtree_finddata(home_servers_byname, &myhome);
+               if (home) {
+                       *phome = home;
+                       return 1;
+               }
+               break;
+
+       default:
+               break;
+       }
+
        cf_log_err_cp(cp, "Unknown home_server \"%s\".", name);
        return 0;
 }
@@ -1229,13 +1270,21 @@ static int server_pool_add(realm_config_t *rc,
 
                home = rbtree_finddata(home_servers_byname, &myhome);
                if (!home) {
-                       DEBUG2("Internal sanity check failed");
-                       goto error;
+                       switch (server_type) {
+                       case HOME_TYPE_AUTH:
+                       case HOME_TYPE_ACCT:
+                               myhome.type = HOME_TYPE_AUTH_ACCT;
+                               home = rbtree_finddata(home_servers_byname, &myhome);
+                               break;
+
+                       default:
+                               break;
+                       }
                }
 
-               if (0) {
-                       WARN("Duplicate home server %s in server pool %s", home->name, pool->name);
-                       continue;
+               if (!home) {
+                       ERROR("Failed to find home server %s", value);
+                       goto error;
                }
 
                if (do_print) cf_log_info(cs, "\thome_server = %s", home->name);
@@ -1317,6 +1366,8 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
        myhome.type = type;
        home = rbtree_finddata(home_servers_byname, &myhome);
        if (home) {
+               WARN("Please use pools instead of authhost and accthost");
+
                if (secret && (strcmp(home->secret, secret) != 0)) {
                        cf_log_err_cs(cs, "Inconsistent shared secret for home server \"%s\"", name);
                        return 0;
@@ -1641,7 +1692,13 @@ static int old_realm_config(realm_config_t *rc, CONF_SECTION *cs, REALM *r)
                }
        }
 
-       if (secret) cf_log_info(cs, "\tsecret = %s", secret);
+       if (secret) {
+               if (rad_debug_lvl <= 2) {
+                       cf_log_info(cs, "\tsecret = <<< secret >>>");
+               } else {
+                       cf_log_info(cs, "\tsecret = %s", secret);
+               }
+       }
 
        return 1;
 
@@ -1888,7 +1945,6 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
 
  error:
        cf_log_info(cs, " } # realm %s", name2);
-       free(r);
        return 0;
 }
 
@@ -2260,7 +2316,7 @@ void home_server_update_request(home_server_t *home, REQUEST *request)
                 *      attribute is the one hacked through
                 *      the 'hints' file.
                 */
-               request->proxy->vps = paircopy(request->proxy,
+               request->proxy->vps = fr_pair_list_copy(request->proxy,
                                               request->packet->vps);
        }
 
@@ -2281,8 +2337,8 @@ void home_server_update_request(home_server_t *home, REQUEST *request)
         *      unless one already exists.
         */
        if ((request->packet->code == PW_CODE_ACCESS_REQUEST) &&
-           !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY)) {
-               pairmake(request->proxy, &request->proxy->vps,
+           !fr_pair_find_by_num(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY)) {
+               fr_pair_make(request->proxy, &request->proxy->vps,
                         "Message-Authenticator", "0x00",
                         T_OP_SET);
        }
@@ -2353,7 +2409,7 @@ home_server_t *home_server_ldb(char const *realmname,
                break;
 
        case HOME_POOL_KEYED_BALANCE:
-               if ((vp = pairfind(request->config_items, PW_LOAD_BALANCE_KEY, 0, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_num(request->config, PW_LOAD_BALANCE_KEY, 0, TAG_ANY)) != NULL) {
                        hash = fr_hash(vp->vp_strvalue, vp->vp_length);
                        start = hash % pool->num_home_servers;
                        break;
index 1ca330f..10ffd57 100644 (file)
@@ -63,10 +63,10 @@ void regex_sub_to_request(REQUEST *request, regex_t **preg, char const *value, s
         */
        old_sc = request_data_get(request, request, REQUEST_DATA_REGEX);
        if (old_sc) {
-               RDEBUG4("Clearing %zu matches", old_sc->nmatch);
+               DEBUG4("Clearing %zu matches", old_sc->nmatch);
                talloc_free(old_sc);
        } else {
-               RDEBUG4("No matches");
+               DEBUG4("No matches");
        }
 
        if (nmatch == 0) return;
@@ -74,7 +74,8 @@ void regex_sub_to_request(REQUEST *request, regex_t **preg, char const *value, s
        rad_assert(preg && *preg);
        rad_assert(rxmatch);
 
-       RDEBUG4("Adding %zu matches", nmatch);
+       DEBUG4("Adding %zu matches", nmatch);
+
        /*
         *      Add new_sc matches
         */
index 2e708c1..6e0d31c 100644 (file)
@@ -51,14 +51,14 @@ int session_zap(REQUEST *request, uint32_t nasaddr, uint32_t nas_port,
 
        /* Hold your breath */
 #define PAIR(n,v,e) do { \
-               if(!(vp = paircreate(stopreq->packet,n, 0))) {  \
+               if(!(vp = fr_pair_afrom_num(stopreq->packet,n, 0))) {   \
                        talloc_free(stopreq); \
                        ERROR("no memory"); \
-                       pairfree(&(stopreq->packet->vps)); \
+                       fr_pair_list_free(&(stopreq->packet->vps)); \
                        return 0; \
                } \
                vp->e = v; \
-               pairadd(&(stopreq->packet->vps), vp); \
+               fr_pair_add(&(stopreq->packet->vps), vp); \
        } while(0)
 
 #define INTPAIR(n,v) PAIR(n,v,vp_integer)
@@ -66,14 +66,14 @@ int session_zap(REQUEST *request, uint32_t nasaddr, uint32_t nas_port,
 #define IPPAIR(n,v) PAIR(n,v,vp_ipaddr)
 
 #define STRINGPAIR(n,v) do { \
-         if(!(vp = paircreate(stopreq->packet,n, 0))) {        \
+         if(!(vp = fr_pair_afrom_num(stopreq->packet,n, 0))) { \
                talloc_free(stopreq); \
                ERROR("no memory"); \
-               pairfree(&(stopreq->packet->vps)); \
+               fr_pair_list_free(&(stopreq->packet->vps)); \
                return 0; \
        } \
-       pairstrcpy(vp, v);      \
-       pairadd(&(stopreq->packet->vps), vp); \
+       fr_pair_value_strcpy(vp, v);    \
+       fr_pair_add(&(stopreq->packet->vps), vp); \
        } while(0)
 
        INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
index 945b295..ab2bad0 100644 (file)
@@ -162,35 +162,35 @@ static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_le
                        }
                        data_len -= 18;
 
-                       vp = pairmake_packet("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-OS-vendor", "Microsoft", T_OP_EQ);
                        if (!vp) return 0;
 
-                       vp = pairmake_packet("SoH-MS-Machine-OS-version", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-OS-version", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_integer = soh_pull_be_32(p); p+=4;
 
-                       vp = pairmake_packet("SoH-MS-Machine-OS-release", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-OS-release", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_integer = soh_pull_be_32(p); p+=4;
 
-                       vp = pairmake_packet("SoH-MS-Machine-OS-build", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-OS-build", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_integer = soh_pull_be_32(p); p+=4;
 
-                       vp = pairmake_packet("SoH-MS-Machine-SP-version", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-SP-version", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_integer = soh_pull_be_16(p); p+=2;
 
-                       vp = pairmake_packet("SoH-MS-Machine-SP-release", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-SP-release", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_integer = soh_pull_be_16(p); p+=2;
 
-                       vp = pairmake_packet("SoH-MS-Machine-Processor", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-Processor", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_integer = soh_pull_be_16(p); p+=2;
@@ -245,7 +245,7 @@ static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_le
                        t = soh_pull_be_16(p);
                        p += 2;
 
-                       vp = pairmake_packet("SoH-MS-Machine-Name", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-Name", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_strvalue = q = talloc_array(vp, char, t);
@@ -265,10 +265,10 @@ static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_le
                         * 24 bytes opaque binary which we might, in future, have
                         * to echo back to the client in a final SoHR
                         */
-                       vp = pairmake_packet("SoH-MS-Correlation-Id", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Correlation-Id", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
-                       pairmemcpy(vp, p, 24);
+                       fr_pair_value_memcpy(vp, p, 24);
                        p += 24;
                        data_len -= 24;
                        break;
@@ -294,7 +294,7 @@ static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_le
                         * 1 byte product type (client=1 domain_controller=2 server=3)
                         */
                        p += 4;
-                       vp = pairmake_packet("SoH-MS-Machine-Role", NULL, T_OP_EQ);
+                       vp = pair_make_request("SoH-MS-Machine-Role", NULL, T_OP_EQ);
                        if (!vp) return 0;
 
                        vp->vp_integer = *p;
@@ -543,7 +543,7 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
 
                                RDEBUG2("SoH Health-Class-Status microsoft DWORD=%08x", hcstatus);
 
-                               vp = pairmake_packet("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ);
+                               vp = pair_make_request("SoH-MS-Windows-Health-Status", NULL, T_OP_EQ);
                                if (!vp) return 0;
 
                                switch (curr_hc) {
@@ -552,39 +552,39 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                        s = "security-updates";
                                        switch (hcstatus) {
                                        case 0xff0005:
-                                               pairsprintf(vp, "%s ok all-installed", s);
+                                               fr_pair_value_sprintf(vp, "%s ok all-installed", s);
                                                break;
 
                                        case 0xff0006:
-                                               pairsprintf(vp, "%s warn some-missing", s);
+                                               fr_pair_value_sprintf(vp, "%s warn some-missing", s);
                                                break;
 
                                        case 0xff0008:
-                                               pairsprintf(vp, "%s warn never-started", s);
+                                               fr_pair_value_sprintf(vp, "%s warn never-started", s);
                                                break;
 
                                        case 0xc0ff000c:
-                                               pairsprintf(vp, "%s error no-wsus-srv", s);
+                                               fr_pair_value_sprintf(vp, "%s error no-wsus-srv", s);
                                                break;
 
                                        case 0xc0ff000d:
-                                               pairsprintf(vp, "%s error no-wsus-clid", s);
+                                               fr_pair_value_sprintf(vp, "%s error no-wsus-clid", s);
                                                break;
 
                                        case 0xc0ff000e:
-                                               pairsprintf(vp, "%s warn wsus-disabled", s);
+                                               fr_pair_value_sprintf(vp, "%s warn wsus-disabled", s);
                                                break;
 
                                        case 0xc0ff000f:
-                                               pairsprintf(vp, "%s error comm-failure", s);
+                                               fr_pair_value_sprintf(vp, "%s error comm-failure", s);
                                                break;
 
                                        case 0xc0ff0010:
-                                               pairsprintf(vp, "%s warn needs-reboot", s);
+                                               fr_pair_value_sprintf(vp, "%s warn needs-reboot", s);
                                                break;
 
                                        default:
-                                               pairsprintf(vp, "%s error %08x", s, hcstatus);
+                                               fr_pair_value_sprintf(vp, "%s error %08x", s, hcstatus);
                                                break;
                                        }
                                        break;
@@ -594,35 +594,35 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                        s = "auto-updates";
                                        switch (hcstatus) {
                                        case 1:
-                                               pairsprintf(vp, "%s warn disabled", s);
+                                               fr_pair_value_sprintf(vp, "%s warn disabled", s);
                                                break;
 
                                        case 2:
-                                               pairsprintf(vp, "%s ok action=check-only", s);
+                                               fr_pair_value_sprintf(vp, "%s ok action=check-only", s);
                                                break;
 
                                        case 3:
-                                               pairsprintf(vp, "%s ok action=download", s);
+                                               fr_pair_value_sprintf(vp, "%s ok action=download", s);
                                                break;
 
                                        case 4:
-                                               pairsprintf(vp, "%s ok action=install", s);
+                                               fr_pair_value_sprintf(vp, "%s ok action=install", s);
                                                break;
 
                                        case 5:
-                                               pairsprintf(vp, "%s warn unconfigured", s);
+                                               fr_pair_value_sprintf(vp, "%s warn unconfigured", s);
                                                break;
 
                                        case 0xc0ff0003:
-                                               pairsprintf(vp, "%s warn service-down", s);
+                                               fr_pair_value_sprintf(vp, "%s warn service-down", s);
                                                break;
 
                                        case 0xc0ff0018:
-                                               pairsprintf(vp, "%s warn never-started", s);
+                                               fr_pair_value_sprintf(vp, "%s warn never-started", s);
                                                break;
 
                                        default:
-                                               pairsprintf(vp, "%s error %08x", s, hcstatus);
+                                               fr_pair_value_sprintf(vp, "%s error %08x", s, hcstatus);
                                                break;
                                        }
                                        break;
@@ -639,12 +639,12 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                                         */
                                                        t = clientstatus2str(hcstatus);
                                                        if (t) {
-                                                               pairsprintf(vp, "%s error %s", s, t);
+                                                               fr_pair_value_sprintf(vp, "%s error %s", s, t);
                                                        } else {
-                                                               pairsprintf(vp, "%s error %08x", s, hcstatus);
+                                                               fr_pair_value_sprintf(vp, "%s error %08x", s, hcstatus);
                                                        }
                                                } else {
-                                                       pairsprintf(vp,
+                                                       fr_pair_value_sprintf(vp,
                                                                        "%s ok snoozed=%i microsoft=%i up2date=%i enabled=%i",
                                                                        s,
                                                                        hcstatus & 0x8 ? 1 : 0,
@@ -654,16 +654,16 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
                                                                        );
                                                }
                                        } else {
-                                               pairsprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
+                                               fr_pair_value_sprintf(vp, "%i unknown %08x", curr_hc, hcstatus);
                                        }
                                        break;
                                }
                        } else {
-                               vp = pairmake_packet("SoH-MS-Health-Other", NULL, T_OP_EQ);
+                               vp = pair_make_request("SoH-MS-Health-Other", NULL, T_OP_EQ);
                                if (!vp) return 0;
 
                                /* FIXME: what to do with the payload? */
-                               pairsprintf(vp, "%08x/%i ?", curr_shid, curr_shid_c);
+                               fr_pair_value_sprintf(vp, "%08x/%i ?", curr_shid, curr_shid_c);
                        }
                        break;
 
index 6bed8cc..e292e04 100644 (file)
@@ -30,23 +30,6 @@ RCSID("$Id$")
 #include <freeradius-devel/state.h>
 #include <freeradius-devel/rad_assert.h>
 
-static rbtree_t *state_tree;
-
-#ifdef HAVE_PTHREAD_H
-static pthread_mutex_t state_mutex;
-
-#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
-
 typedef struct state_entry_t {
        uint8_t         state[AUTH_VECTOR_LEN];
 
@@ -62,8 +45,31 @@ typedef struct state_entry_t {
        void            (*free_opaque)(void *opaque);
 } state_entry_t;
 
-static state_entry_t *state_head = NULL;
-static state_entry_t *state_tail = NULL;
+struct fr_state_t {
+       rbtree_t *tree;
+
+       state_entry_t *head, *tail;
+
+#ifdef HAVE_PTHREAD_H
+       pthread_mutex_t mutex;
+#endif
+};
+
+static fr_state_t global_state;
+
+#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
 
 /*
  *     rbtree callback.
@@ -82,7 +88,7 @@ static int state_entry_cmp(void const *one, void const *two)
  *
  *     Note that
  */
-static void state_entry_free(state_entry_t *entry)
+static void state_entry_free(fr_state_t *state, state_entry_t *entry)
 {
        state_entry_t *prev, *next;
 
@@ -90,25 +96,25 @@ static void state_entry_free(state_entry_t *entry)
         *      If we're deleting the whole tree, don't bother doing
         *      all of the fixups.
         */
-       if (!state_tree) return;
+       if (!state || !state->tree) return;
 
        prev = entry->prev;
        next = entry->next;
 
        if (prev) {
-               rad_assert(state_tail != entry);
+               rad_assert(state->head != entry);
                prev->next = next;
-       } else if (state_head) {
-               rad_assert(state_head == entry);
-               state_head = next;
+       } else if (state->head) {
+               rad_assert(state->head == entry);
+               state->head = next;
        }
 
        if (next) {
-               rad_assert(state_tail != entry);
+               rad_assert(state->tail != entry);
                next->prev = prev;
-       } else if (state_tail) {
-               rad_assert(state_tail == entry);
-               state_tail = prev;
+       } else if (state->tail) {
+               rad_assert(state->tail == entry);
+               state->tail = prev;
        }
 
        if (entry->opaque) {
@@ -118,47 +124,63 @@ static void state_entry_free(state_entry_t *entry)
 #ifdef WITH_VERIFY_PTR
        (void) talloc_get_type_abort(entry, state_entry_t);
 #endif
-       rbtree_deletebydata(state_tree, entry);
+       rbtree_deletebydata(state->tree, entry);
        talloc_free(entry);
 }
 
-bool fr_state_init(void)
+fr_state_t *fr_state_init(TALLOC_CTX *ctx)
 {
+       fr_state_t *state;
+
+       if (!ctx) {
+               state = &global_state;
+               if (state->tree) return state;
+       } else {
+               state = talloc_zero(ctx, fr_state_t);
+               if (!state) return 0;
+       }
+
 #ifdef HAVE_PTHREAD_H
-       if (pthread_mutex_init(&state_mutex, NULL) != 0) {
-               return false;
+       if (pthread_mutex_init(&state->mutex, NULL) != 0) {
+               talloc_free(state);
+               return NULL;
        }
 #endif
 
-       state_tree = rbtree_create(NULL, state_entry_cmp, NULL, 0);
-       if (!state_tree) {
-               return false;
+       state->tree = rbtree_create(NULL, state_entry_cmp, NULL, 0);
+       if (!state->tree) {
+               talloc_free(state);
+               return NULL;
        }
 
-       return true;
+       return state;
 }
 
-void fr_state_delete(void)
+void fr_state_delete(fr_state_t *state)
 {
        rbtree_t *my_tree;
 
-       PTHREAD_MUTEX_LOCK(&state_mutex);
+       if (!state) return;
+
+       PTHREAD_MUTEX_LOCK(&state->mutex);
 
        /*
         *      Tell the talloc callback to NOT delete the entry from
         *      the tree.  We're deleting the entire tree.
         */
-       my_tree = state_tree;
-       state_tree = NULL;
+       my_tree = state->tree;
+       state->tree = NULL;
 
        rbtree_free(my_tree);
-       PTHREAD_MUTEX_UNLOCK(&state_mutex);
+       PTHREAD_MUTEX_UNLOCK(&state->mutex);
+
+       if (state != &global_state) talloc_free(state);
 }
 
 /*
  *     Create a new entry.  Called with the mutex held.
  */
-static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
+static state_entry_t *fr_state_create(fr_state_t *state, RADIUS_PACKET *packet, state_entry_t *old)
 {
        size_t i;
        uint32_t x;
@@ -169,7 +191,7 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
        /*
         *      Clean up old entries.
         */
-       for (entry = state_head; entry != NULL; entry = next) {
+       for (entry = state->head; entry != NULL; entry = next) {
                next = entry->next;
 
                if (entry == old) continue;
@@ -178,7 +200,7 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
                 *      Too old, we can delete it.
                 */
                if (entry->cleanup < now) {
-                       state_entry_free(entry);
+                       state_entry_free(state, entry);
                        continue;
                }
 
@@ -187,7 +209,7 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
                 *      the time to clean it up.
                 */
                if (!entry->vps && !entry->opaque) {
-                       state_entry_free(entry);
+                       state_entry_free(state, entry);
                        continue;
                }
 
@@ -198,14 +220,14 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
         *      Limit the size of the cache based on how many requests
         *      we can handle at the same time.
         */
-       if (rbtree_num_elements(state_tree) >= main_config.max_requests * 2) {
+       if (rbtree_num_elements(state->tree) >= main_config.max_requests * 2) {
                return NULL;
        }
 
        /*
         *      Allocate a new one.
         */
-       entry = talloc_zero(state_tree, state_entry_t);
+       entry = talloc_zero(state->tree, state_entry_t);
        if (!entry) return NULL;
 
        /*
@@ -222,7 +244,7 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
         *      The EAP module creates it's own State attribute, so we
         *      want to use that one in preference to one we create.
         */
-       vp = pairfind(packet->vps, PW_STATE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_STATE, 0, TAG_ANY);
 
        /*
         *      If possible, base the new one off of the old one.
@@ -239,13 +261,15 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
                        memcpy(entry->state, old->state, sizeof(entry->state));
 
                        entry->state[1] = entry->state[0] ^ entry->tries;
-                       entry->state[3] = entry->state[2] ^ (RADIUSD_VERSION / 10000);
+                       entry->state[8] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 16) & 0xff);
+                       entry->state[10] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 8) & 0xff);
+                       entry->state[12] = entry->state[2] ^ (((uint32_t) HEXIFY(RADIUSD_VERSION)) & 0xff);
                }
 
                /*
                 *      The old one isn't used any more, so we can free it.
                 */
-               if (!old->opaque) state_entry_free(old);
+               if (!old->opaque) state_entry_free(state, old);
 
        } else if (!vp) {
                /*
@@ -263,19 +287,19 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
         *      one we created above.
         */
        if (vp) {
-               if (debug_flag && (vp->vp_length > sizeof(entry->state))) {
+               if (rad_debug_lvl && (vp->vp_length > sizeof(entry->state))) {
                        WARN("State should be %zd octets!",
                             sizeof(entry->state));
                }
                memcpy(entry->state, vp->vp_octets, sizeof(entry->state));
 
        } else {
-               vp = paircreate(packet, PW_STATE, 0);
-               pairmemcpy(vp, entry->state, sizeof(entry->state));
-               pairadd(&packet->vps, vp);
+               vp = fr_pair_afrom_num(packet, PW_STATE, 0);
+               fr_pair_value_memcpy(vp, entry->state, sizeof(entry->state));
+               fr_pair_add(&packet->vps, vp);
        }
 
-       if (!rbtree_insert(state_tree, entry)) {
+       if (!rbtree_insert(state->tree, entry)) {
                talloc_free(entry);
                return NULL;
        }
@@ -284,17 +308,17 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
         *      Link it to the end of the list, which is implicitely
         *      ordered by cleanup time.
         */
-       if (!state_head) {
+       if (!state->head) {
                entry->prev = entry->next = NULL;
-               state_head = state_tail = entry;
+               state->head = state->tail = entry;
        } else {
-               rad_assert(state_tail != NULL);
+               rad_assert(state->tail != NULL);
 
-               entry->prev = state_tail;
-               state_tail->next = entry;
+               entry->prev = state->tail;
+               state->tail->next = entry;
 
                entry->next = NULL;
-               state_tail = entry;
+               state->tail = entry;
        }
 
        return entry;
@@ -304,19 +328,19 @@ static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
 /*
  *     Find the entry, based on the State attribute.
  */
-static state_entry_t *fr_state_find(RADIUS_PACKET *packet)
+static state_entry_t *fr_state_find(fr_state_t *state, RADIUS_PACKET *packet)
 {
        VALUE_PAIR *vp;
        state_entry_t *entry, my_entry;
 
-       vp = pairfind(packet->vps, PW_STATE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_STATE, 0, TAG_ANY);
        if (!vp) return NULL;
 
        if (vp->vp_length != sizeof(my_entry.state)) return NULL;
 
        memcpy(my_entry.state, vp->vp_octets, sizeof(my_entry.state));
 
-       entry = rbtree_finddata(state_tree, &my_entry);
+       entry = rbtree_finddata(state->tree, &my_entry);
 
 #ifdef WITH_VERIFY_PTR
        if (entry)  (void) talloc_get_type_abort(entry, state_entry_t);
@@ -332,19 +356,20 @@ static state_entry_t *fr_state_find(RADIUS_PACKET *packet)
 void fr_state_discard(REQUEST *request, RADIUS_PACKET *original)
 {
        state_entry_t *entry;
+       fr_state_t *state = &global_state;
 
-       pairfree(&request->state);
+       fr_pair_list_free(&request->state);
        request->state = NULL;
 
-       PTHREAD_MUTEX_LOCK(&state_mutex);
-       entry = fr_state_find(original);
+       PTHREAD_MUTEX_LOCK(&state->mutex);
+       entry = fr_state_find(state, original);
        if (!entry) {
-               PTHREAD_MUTEX_UNLOCK(&state_mutex);
+               PTHREAD_MUTEX_UNLOCK(&state->mutex);
                return;
        }
 
-       state_entry_free(entry);
-       PTHREAD_MUTEX_UNLOCK(&state_mutex);
+       state_entry_free(state, entry);
+       PTHREAD_MUTEX_UNLOCK(&state->mutex);
        return;
 }
 
@@ -354,26 +379,27 @@ void fr_state_discard(REQUEST *request, RADIUS_PACKET *original)
 void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet)
 {
        state_entry_t *entry;
+       fr_state_t *state = &global_state;
 
        rad_assert(request->state == NULL);
 
        /*
         *      No State, don't do anything.
         */
-       if (!pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY)) {
+       if (!fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY)) {
                RDEBUG3("session-state: No State attribute");
                return;
        }
 
-       PTHREAD_MUTEX_LOCK(&state_mutex);
-       entry = fr_state_find(packet);
+       PTHREAD_MUTEX_LOCK(&state->mutex);
+       entry = fr_state_find(state, packet);
 
        /*
         *      This has to be done in a mutex lock, because talloc
         *      isn't thread-safe.
         */
        if (entry) {
-               pairfilter(request, &request->state, &entry->vps, 0, 0, TAG_ANY);
+               fr_pair_list_mcopy_by_num(request, &request->state, &entry->vps, 0, 0, TAG_ANY);
                RDEBUG2("session-state: Found cached attributes");
                rdebug_pair_list(L_DBG_LVL_1, request, request->state, NULL);
 
@@ -381,7 +407,7 @@ void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet)
                RDEBUG2("session-state: No cached attributes");
        }
 
-       PTHREAD_MUTEX_UNLOCK(&state_mutex);
+       PTHREAD_MUTEX_UNLOCK(&state->mutex);
 
        VERIFY_REQUEST(request);
        return;
@@ -396,6 +422,7 @@ void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet)
 bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet)
 {
        state_entry_t *entry, *old;
+       fr_state_t *state = &global_state;
 
        if (!request->state) {
                RDEBUG3("session-state: Nothing to cache");
@@ -405,17 +432,17 @@ bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *
        RDEBUG2("session-state: Saving cached attributes");
        rdebug_pair_list(L_DBG_LVL_1, request, request->state, NULL);
 
-       PTHREAD_MUTEX_LOCK(&state_mutex);
+       PTHREAD_MUTEX_LOCK(&state->mutex);
 
        if (original) {
-               old = fr_state_find(original);
+               old = fr_state_find(state, original);
        } else {
                old = NULL;
        }
 
-       entry = fr_state_create(packet, old);
+       entry = fr_state_create(state, packet, old);
        if (!entry) {
-               PTHREAD_MUTEX_UNLOCK(&state_mutex);
+               PTHREAD_MUTEX_UNLOCK(&state->mutex);
                return false;
        }
 
@@ -423,8 +450,8 @@ bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *
         *      This has to be done in a mutex lock, because talloc
         *      isn't thread-safe.
         */
-       pairfilter(entry, &entry->vps, &request->state, 0, 0, TAG_ANY);
-       PTHREAD_MUTEX_UNLOCK(&state_mutex);
+       fr_pair_list_mcopy_by_num(entry, &entry->vps, &request->state, 0, 0, TAG_ANY);
+       PTHREAD_MUTEX_UNLOCK(&state->mutex);
 
        rad_assert(request->state == NULL);
        VERIFY_REQUEST(request);
@@ -435,20 +462,22 @@ bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *
  *     Find the opaque data associated with a State attribute.
  *     Leave the data in the entry.
  */
-void *fr_state_find_data(UNUSED REQUEST *request, RADIUS_PACKET *packet)
+void *fr_state_find_data(fr_state_t *state, UNUSED REQUEST *request, RADIUS_PACKET *packet)
 {
        void *data;
        state_entry_t *entry;
 
-       PTHREAD_MUTEX_LOCK(&state_mutex);
-       entry = fr_state_find(packet);
+       if (!state) return false;
+
+       PTHREAD_MUTEX_LOCK(&state->mutex);
+       entry = fr_state_find(state, packet);
        if (!entry) {
-               PTHREAD_MUTEX_UNLOCK(&state_mutex);
+               PTHREAD_MUTEX_UNLOCK(&state->mutex);
                return NULL;
        }
 
        data = entry->opaque;
-       PTHREAD_MUTEX_UNLOCK(&state_mutex);
+       PTHREAD_MUTEX_UNLOCK(&state->mutex);
 
        return data;
 }
@@ -458,21 +487,23 @@ void *fr_state_find_data(UNUSED REQUEST *request, RADIUS_PACKET *packet)
  *     Get the opaque data associated with a State attribute.
  *     and remove the data from the entry.
  */
-void *fr_state_get_data(UNUSED REQUEST *request, RADIUS_PACKET *packet)
+void *fr_state_get_data(fr_state_t *state, UNUSED REQUEST *request, RADIUS_PACKET *packet)
 {
        void *data;
        state_entry_t *entry;
 
-       PTHREAD_MUTEX_LOCK(&state_mutex);
-       entry = fr_state_find(packet);
+       if (!state) return NULL;
+
+       PTHREAD_MUTEX_LOCK(&state->mutex);
+       entry = fr_state_find(state, packet);
        if (!entry) {
-               PTHREAD_MUTEX_UNLOCK(&state_mutex);
+               PTHREAD_MUTEX_UNLOCK(&state->mutex);
                return NULL;
        }
 
        data = entry->opaque;
        entry->opaque = NULL;
-       PTHREAD_MUTEX_UNLOCK(&state_mutex);
+       PTHREAD_MUTEX_UNLOCK(&state->mutex);
 
        return data;
 }
@@ -482,22 +513,24 @@ void *fr_state_get_data(UNUSED REQUEST *request, RADIUS_PACKET *packet)
  *     Get the opaque data associated with a State attribute.
  *     and remove the data from the entry.
  */
-bool fr_state_put_data(UNUSED REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet,
+bool fr_state_put_data(fr_state_t *state, UNUSED REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet,
                       void *data, void (*free_data)(void *))
 {
        state_entry_t *entry, *old;
 
-       PTHREAD_MUTEX_LOCK(&state_mutex);
+       if (!state) return false;
+
+       PTHREAD_MUTEX_LOCK(&state->mutex);
 
        if (original) {
-               old = fr_state_find(original);
+               old = fr_state_find(state, original);
        } else {
                old = NULL;
        }
 
-       entry = fr_state_create(packet, old);
+       entry = fr_state_create(state, packet, old);
        if (!entry) {
-               PTHREAD_MUTEX_UNLOCK(&state_mutex);
+               PTHREAD_MUTEX_UNLOCK(&state->mutex);
                return false;
        }
 
@@ -512,6 +545,6 @@ bool fr_state_put_data(UNUSED REQUEST *request, RADIUS_PACKET *original, RADIUS_
        entry->opaque = data;
        entry->free_opaque = free_data;
 
-       PTHREAD_MUTEX_UNLOCK(&state_mutex);
+       PTHREAD_MUTEX_UNLOCK(&state->mutex);
        return true;
 }
index 981fa38..7f85718 100644 (file)
@@ -255,19 +255,29 @@ void request_stats_final(REQUEST *request)
        }
 
 #ifdef WITH_PROXY
-       if (!request->proxy || !request->proxy_listener) goto done;     /* simplifies formatting */
+       if (!request->proxy || !request->home_server) goto done;        /* simplifies formatting */
 
        switch (request->proxy->code) {
        case PW_CODE_ACCESS_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_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;
+               break;
+#endif
+
+#ifdef WITH_COA
+       case PW_CODE_COA_REQUEST:
+               proxy_coa_stats.total_requests++;
+               request->home_server->stats.total_requests += request->num_proxied_requests;
+               break;
+
+       case PW_CODE_DISCONNECT_REQUEST:
+               proxy_dsc_stats.total_requests++;
                request->home_server->stats.total_requests += request->num_proxied_requests;
                break;
 #endif
@@ -279,7 +289,7 @@ void request_stats_final(REQUEST *request)
        if (!request->proxy_reply) goto done;   /* simplifies formatting */
 
 #undef INC
-#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;
+#define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses;
 
        switch (request->proxy_reply->code) {
        case PW_CODE_ACCESS_ACCEPT:
@@ -305,7 +315,32 @@ void request_stats_final(REQUEST *request)
 #ifdef WITH_ACCOUNTING
        case PW_CODE_ACCOUNTING_RESPONSE:
                proxy_acct_stats.total_responses++;
-               request->proxy_listener->stats.total_responses++;
+               request->home_server->stats.total_responses++;
+               stats_time(&proxy_acct_stats,
+                          &request->proxy->timestamp,
+                          &request->proxy_reply->timestamp);
+               stats_time(&request->home_server->stats,
+                          &request->proxy->timestamp,
+                          &request->proxy_reply->timestamp);
+               break;
+#endif
+
+#ifdef WITH_COA
+       case PW_CODE_COA_ACK:
+       case PW_CODE_COA_NAK:
+               proxy_coa_stats.total_responses++;
+               request->home_server->stats.total_responses++;
+               stats_time(&proxy_acct_stats,
+                          &request->proxy->timestamp,
+                          &request->proxy_reply->timestamp);
+               stats_time(&request->home_server->stats,
+                          &request->proxy->timestamp,
+                          &request->proxy_reply->timestamp);
+               break;
+
+       case PW_CODE_DISCONNECT_ACK:
+       case PW_CODE_DISCONNECT_NAK:
+               proxy_coa_stats.total_responses++;
                request->home_server->stats.total_responses++;
                stats_time(&proxy_acct_stats,
                           &request->proxy->timestamp,
@@ -318,7 +353,6 @@ void request_stats_final(REQUEST *request)
 
        default:
                proxy_auth_stats.total_unknown_types++;
-               request->proxy_listener->stats.total_unknown_types++;
                request->home_server->stats.total_unknown_types++;
                break;
        }
@@ -436,7 +470,7 @@ static void request_stats_addvp(REQUEST *request,
        VALUE_PAIR *vp;
 
        for (i = 0; table[i].attribute != 0; i++) {
-               vp = radius_paircreate(request->reply, &request->reply->vps,
+               vp = radius_pair_create(request->reply, &request->reply->vps,
                                       table[i].attribute, VENDORPEC_FREERADIUS);
                if (!vp) continue;
 
@@ -456,7 +490,7 @@ void request_stats_reply(REQUEST *request)
        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);
+       flag = fr_pair_find_by_num(request->packet->vps, 127, VENDORPEC_FREERADIUS, TAG_ANY);
        if (!flag || (flag->vp_integer == 0)) return;
 
        /*
@@ -501,10 +535,10 @@ void request_stats_reply(REQUEST *request)
         *      Internal server statistics
         */
        if ((flag->vp_integer & 0x10) != 0) {
-               vp = radius_paircreate(request->reply, &request->reply->vps,
+               vp = radius_pair_create(request->reply, &request->reply->vps,
                                       176, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = start_time.tv_sec;
-               vp = radius_paircreate(request->reply, &request->reply->vps,
+               vp = radius_pair_create(request->reply, &request->reply->vps,
                                       177, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = hup_time.tv_sec;
 
@@ -514,7 +548,7 @@ void request_stats_reply(REQUEST *request)
                thread_pool_queue_stats(array, pps);
 
                for (i = 0; i <= 4; i++) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               162 + i, VENDORPEC_FREERADIUS);
 
                        if (!vp) continue;
@@ -522,7 +556,7 @@ void request_stats_reply(REQUEST *request)
                }
 
                for (i = 0; i < 2; i++) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               181 + i, VENDORPEC_FREERADIUS);
 
                        if (!vp) continue;
@@ -544,9 +578,9 @@ void request_stats_reply(REQUEST *request)
                 *      See if we need to look up the client by server
                 *      socket.
                 */
-               server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
+               server_ip = fr_pair_find_by_num(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
                if (server_ip) {
-                       server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
+                       server_port = fr_pair_find_by_num(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
 
                        if (server_port) {
                                ipaddr.af = AF_INET;
@@ -561,7 +595,7 @@ void request_stats_reply(REQUEST *request)
                }
 
 
-               vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, 167, VENDORPEC_FREERADIUS, TAG_ANY);
                if (vp) {
                        memset(&ipaddr, 0, sizeof(ipaddr));
                        ipaddr.af = AF_INET;
@@ -576,7 +610,7 @@ void request_stats_reply(REQUEST *request)
                        /*
                         *      Else look it up by number.
                         */
-               } else if ((vp = pairfind(request->packet->vps, 168, VENDORPEC_FREERADIUS, TAG_ANY)) != NULL) {
+               } else if ((vp = fr_pair_find_by_num(request->packet->vps, 168, VENDORPEC_FREERADIUS, TAG_ANY)) != NULL) {
                        client = client_findbynumber(cl, vp->vp_integer);
                }
 
@@ -585,7 +619,7 @@ void request_stats_reply(REQUEST *request)
                         *      If found, echo it back, along with
                         *      the requested statistics.
                         */
-                       pairadd(&request->reply->vps, paircopyvp(request->reply, vp));
+                       fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
 
                        /*
                         *      When retrieving client by number, also
@@ -593,7 +627,7 @@ void request_stats_reply(REQUEST *request)
                         */
                        if ((vp->da->type == PW_TYPE_INTEGER) &&
                            (client->ipaddr.af == AF_INET)) {
-                               vp = radius_paircreate(request->reply,
+                               vp = radius_pair_create(request->reply,
                                                       &request->reply->vps,
                                                       167, VENDORPEC_FREERADIUS);
                                if (vp) {
@@ -601,7 +635,7 @@ void request_stats_reply(REQUEST *request)
                                }
 
                                if (client->ipaddr.prefix != 32) {
-                                       vp = radius_paircreate(request->reply,
+                                       vp = radius_pair_create(request->reply,
                                                               &request->reply->vps,
                                                               169, VENDORPEC_FREERADIUS);
                                        if (vp) {
@@ -611,12 +645,12 @@ void request_stats_reply(REQUEST *request)
                        }
 
                        if (server_ip) {
-                               pairadd(&request->reply->vps,
-                                       paircopyvp(request->reply, server_ip));
+                               fr_pair_add(&request->reply->vps,
+                                       fr_pair_copy(request->reply, server_ip));
                        }
                        if (server_port) {
-                               pairadd(&request->reply->vps,
-                                       paircopyvp(request->reply, server_port));
+                               fr_pair_add(&request->reply->vps,
+                                       fr_pair_copy(request->reply, server_port));
                        }
 
                        if ((flag->vp_integer & 0x01) != 0) {
@@ -645,10 +679,10 @@ void request_stats_reply(REQUEST *request)
                 *      See if we need to look up the server by socket
                 *      socket.
                 */
-               server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
+               server_ip = fr_pair_find_by_num(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
                if (!server_ip) return;
 
-               server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
+               server_port = fr_pair_find_by_num(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
                if (!server_port) return;
 
                ipaddr.af = AF_INET;
@@ -662,10 +696,10 @@ void request_stats_reply(REQUEST *request)
                 */
                if (!this) return;
 
-               pairadd(&request->reply->vps,
-                       paircopyvp(request->reply, server_ip));
-               pairadd(&request->reply->vps,
-                       paircopyvp(request->reply, server_port));
+               fr_pair_add(&request->reply->vps,
+                       fr_pair_copy(request->reply, server_ip));
+               fr_pair_add(&request->reply->vps,
+                       fr_pair_copy(request->reply, server_port));
 
                if (((flag->vp_integer & 0x01) != 0) &&
                    ((request->listener->type == RAD_LISTEN_AUTH) ||
@@ -696,10 +730,10 @@ void request_stats_reply(REQUEST *request)
                 *      See if we need to look up the server by socket
                 *      socket.
                 */
-               server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
+               server_ip = fr_pair_find_by_num(request->packet->vps, 170, VENDORPEC_FREERADIUS, TAG_ANY);
                if (!server_ip) return;
 
-               server_port = pairfind(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
+               server_port = fr_pair_find_by_num(request->packet->vps, 171, VENDORPEC_FREERADIUS, TAG_ANY);
                if (!server_port) return;
 
 #ifndef NDEBUG
@@ -715,37 +749,37 @@ void request_stats_reply(REQUEST *request)
                 */
                if (!home) return;
 
-               pairadd(&request->reply->vps,
-                       paircopyvp(request->reply, server_ip));
-               pairadd(&request->reply->vps,
-                       paircopyvp(request->reply, server_port));
+               fr_pair_add(&request->reply->vps,
+                       fr_pair_copy(request->reply, server_ip));
+               fr_pair_add(&request->reply->vps,
+                       fr_pair_copy(request->reply, server_port));
 
-               vp = radius_paircreate(request->reply, &request->reply->vps,
+               vp = radius_pair_create(request->reply, &request->reply->vps,
                                       172, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_integer = home->currently_outstanding;
 
-               vp = radius_paircreate(request->reply, &request->reply->vps,
+               vp = radius_pair_create(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->reply, &request->reply->vps,
+                       vp = radius_pair_create(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->reply,
+                               vp = radius_pair_create(request->reply,
                                                       &request->reply->vps,
                                                       178, VENDORPEC_FREERADIUS);
                                if (vp) vp->vp_integer = home->ema.window;
-                               vp = radius_paircreate(request->reply,
+                               vp = radius_pair_create(request->reply,
                                                       &request->reply->vps,
                                                       179, VENDORPEC_FREERADIUS);
                                if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
-                               vp = radius_paircreate(request->reply,
+                               vp = radius_pair_create(request->reply,
                                                       &request->reply->vps,
                                                       180, VENDORPEC_FREERADIUS);
                                if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
@@ -753,7 +787,7 @@ void request_stats_reply(REQUEST *request)
                }
 
                if (home->state == HOME_STATE_IS_DEAD) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               174, VENDORPEC_FREERADIUS);
                        if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
                }
@@ -763,11 +797,11 @@ void request_stats_reply(REQUEST *request)
                 *
                 *      FIXME: do this for clients, too!
                 */
-               vp = radius_paircreate(request->reply, &request->reply->vps,
+               vp = radius_pair_create(request->reply, &request->reply->vps,
                                       184, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = home->last_packet_recv;
 
-               vp = radius_paircreate(request->reply, &request->reply->vps,
+               vp = radius_pair_create(request->reply, &request->reply->vps,
                                       185, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = home->last_packet_sent;
 
index 7565145..af91077 100644 (file)
@@ -202,7 +202,7 @@ static const CONF_PARSER thread_config[] = {
        { "auto_limit_acct", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &thread_pool.auto_limit_acct), NULL },
 #endif
 #endif
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 #endif
 
@@ -551,7 +551,7 @@ static int request_dequeue(REQUEST **prequest)
        rad_assert(request->magic == REQUEST_MAGIC);
 
        request->component = "<core>";
-       request->module = "";
+       request->module = "<running>";
        request->child_state = REQUEST_RUNNING;
 
        /*
@@ -669,15 +669,15 @@ static void *request_handler_thread(void *arg)
                        VALUE_PAIR *vp;
                        REQUEST *request = self->request;
 
-                       vp = radius_paircreate(request, &request->config_items,
+                       vp = radius_pair_create(request, &request->config,
                                               181, VENDORPEC_FREERADIUS);
                        if (vp) vp->vp_integer = thread_pool.pps_in.pps;
 
-                       vp = radius_paircreate(request, &request->config_items,
+                       vp = radius_pair_create(request, &request->config,
                                               182, VENDORPEC_FREERADIUS);
                        if (vp) vp->vp_integer = thread_pool.pps_in.pps;
 
-                       vp = radius_paircreate(request, &request->config_items,
+                       vp = radius_pair_create(request, &request->config,
                                               183, VENDORPEC_FREERADIUS);
                        if (vp) {
                                vp->vp_integer = thread_pool.max_queue_size - thread_pool.num_queued;
@@ -1008,7 +1008,7 @@ int thread_pool_init(CONF_SECTION *cs, bool *spawn_flag)
         *      Allocate multiple fifos.
         */
        for (i = 0; i < RAD_LISTEN_MAX; i++) {
-               thread_pool.fifo[i] = fr_fifo_create(thread_pool.max_queue_size, NULL);
+               thread_pool.fifo[i] = fr_fifo_create(NULL, thread_pool.max_queue_size, NULL);
                if (!thread_pool.fifo[i]) {
                        ERROR("FATAL: Failed to set up request fifo");
                        return -1;
@@ -1087,6 +1087,26 @@ void thread_pool_stop(void)
                pthread_join(handle->pthread_id, NULL);
                delete_thread(handle);
        }
+
+       for (i = 0; i < RAD_LISTEN_MAX; i++) {
+               fr_fifo_free(thread_pool.fifo[i]);
+       }
+
+#ifdef WNOHANG
+       fr_hash_table_free(thread_pool.waiters);
+#endif
+
+#ifdef HAVE_OPENSSL_CRYPTO_H
+       /*
+        *      We're no longer threaded.  Remove the mutexes and free
+        *      the memory.
+        */
+       CRYPTO_set_id_callback(NULL);
+       CRYPTO_set_locking_callback(NULL);
+
+       free(ssl_mutexes);
+#endif
+
 #endif
 }
 
@@ -1147,7 +1167,7 @@ static void thread_pool_manage(time_t now)
         */
        active_threads = thread_pool.active_threads;
        spare = thread_pool.total_threads - active_threads;
-       if (debug_flag) {
+       if (rad_debug_lvl) {
                static uint32_t old_total = 0;
                static uint32_t old_active = 0;
 
@@ -1494,7 +1514,7 @@ void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quen
 
        DEBUG("Trigger %s -> %s", name, value);
 
-       radius_exec_program(NULL, 0, NULL, request, value, vp, false, true, EXEC_TIMEOUT);
+       radius_exec_program(request, NULL, 0, NULL, request, value, vp, false, true, EXEC_TIMEOUT);
 
        if (alloc) talloc_free(request);
 }
index 5fce780..d86391f 100644 (file)
@@ -43,13 +43,20 @@ USES_APPLE_DEPRECATED_API   /* OpenSSL API has been deprecated by Apple */
 #include <ctype.h>
 
 #ifdef WITH_TLS
-#ifdef HAVE_OPENSSL_RAND_H
-#include <openssl/rand.h>
-#endif
+#  ifdef HAVE_OPENSSL_RAND_H
+#    include <openssl/rand.h>
+#  endif
 
-#ifdef HAVE_OPENSSL_OCSP_H
-#include <openssl/ocsp.h>
-#endif
+#  ifdef HAVE_OPENSSL_OCSP_H
+#    include <openssl/ocsp.h>
+#  endif
+
+#  ifdef HAVE_OPENSSL_EVP_H
+#    include <openssl/evp.h>
+#  endif
+#  include <openssl/ssl.h>
+
+#define LOG_PREFIX "tls"
 
 #ifdef ENABLE_OPENSSL_VERSION_CHECK
 typedef struct libssl_defect {
@@ -72,14 +79,37 @@ static libssl_defect_t libssl_defects[] =
                .comment        = "For more information see http://heartbleed.com"
        }
 };
-#endif
+#endif /* ENABLE_OPENSSL_VERSION_CHECK */
+
+FR_NAME_NUMBER const fr_tls_status_table[] = {
+       { "invalid",                    FR_TLS_INVALID },
+       { "request",                    FR_TLS_REQUEST },
+       { "response",                   FR_TLS_RESPONSE },
+       { "success",                    FR_TLS_SUCCESS },
+       { "fail",                       FR_TLS_FAIL },
+       { "noop",                       FR_TLS_NOOP },
+
+       { "start",                      FR_TLS_START },
+       { "ok",                         FR_TLS_OK },
+       { "ack",                        FR_TLS_ACK },
+       { "first fragment",             FR_TLS_FIRST_FRAGMENT },
+       { "more fragments",             FR_TLS_MORE_FRAGMENTS },
+       { "length included",            FR_TLS_LENGTH_INCLUDED },
+       { "more fragments with length", FR_TLS_MORE_FRAGMENTS_WITH_LENGTH },
+       { "handled",                    FR_TLS_HANDLED },
+       {  NULL ,                       -1},
+};
 
 /* index we use to store cached session VPs
  * needs to be dynamic so we can supply a "free" function
  */
-static int fr_tls_ex_index_vps = -1;
+int fr_tls_ex_index_vps = -1;
 int fr_tls_ex_index_certs = -1;
 
+/* Session */
+static void            session_close(tls_session_t *ssn);
+static void            session_init(tls_session_t *ssn);
+
 /* record */
 static void            record_init(record_t *buf);
 static void            record_close(record_t *buf);
@@ -139,7 +169,7 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity,
                        return 0;
                }
 
-               vp = pairmake_packet("TLS-PSK-Identity", identity, T_OP_SET);
+               vp = pair_make_request("TLS-PSK-Identity", identity, T_OP_SET);
                if (!vp) return 0;
 
                hex_len = radius_xlat(buffer, sizeof(buffer), request, conf->psk_query,
@@ -278,20 +308,33 @@ tls_session_t *tls_new_client_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *con
                return NULL;
        }
 
-       ssn->offset = conf->fragment_size;
+       ssn->mtu = conf->fragment_size;
 
        return ssn;
 }
 
+/** Create a new TLS session
+ *
+ * Configures a new TLS session, configuring options, setting callbacks etc...
+ *
+ * @param ctx to alloc session data in. Should usually be NULL unless the lifetime of the
+ *     session is tied to another talloc'd object.
+ * @param conf to use to configure the tls session.
+ * @param request The current #REQUEST.
+ * @param client_cert Whether to require a client_cert.
+ * @return a new session on success, or NULL on error.
+ */
 tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQUEST *request, bool client_cert)
 {
-       tls_session_t *state = NULL;
-       SSL *new_tls = NULL;
+       tls_session_t   *state = NULL;
+       SSL             *new_tls = NULL;
        int             verify_mode = 0;
        VALUE_PAIR      *vp;
 
        rad_assert(request != NULL);
 
+       RDEBUG2("Initiating new EAP-TLS session");
+
        /*
         *      Manually flush the sessions every so often.  If HALF
         *      of the session lifetime has passed since we last
@@ -301,16 +344,14 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
         */
        if (conf->session_cache_enable &&
            ((conf->session_last_flushed + ((int)conf->session_timeout * 1800)) <= request->timestamp)){
-               RDEBUG2("Flushing SSL sessions (of #%ld)",
-                       SSL_CTX_sess_number(conf->ctx));
+               RDEBUG2("Flushing SSL sessions (of #%ld)", SSL_CTX_sess_number(conf->ctx));
 
                SSL_CTX_flush_sessions(conf->ctx, request->timestamp);
                conf->session_last_flushed = request->timestamp;
        }
 
        if ((new_tls = SSL_new(conf->ctx)) == NULL) {
-               ERROR("SSL: Error creating new SSL: %s",
-                      ERR_error_string(ERR_get_error(), NULL));
+               RERROR("Error creating new SSL session: %s", ERR_error_string(ERR_get_error(), NULL));
                return NULL;
        }
 
@@ -318,7 +359,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
        SSL_set_app_data(new_tls, NULL);
 
        if ((state = talloc_zero(ctx, tls_session_t)) == NULL) {
-               ERROR("SSL: Error allocating memory for SSL state");
+               RERROR("Error allocating memory for SSL state");
                return NULL;
        }
        session_init(state);
@@ -366,7 +407,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
         *      Verify the peer certificate, if asked.
         */
        if (client_cert) {
-               RDEBUG2("Requiring client certificate");
+               RDEBUG2("Setting verify mode to require certificate from client");
                verify_mode = SSL_VERIFY_PEER;
                verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
                verify_mode |= SSL_VERIFY_CLIENT_ONCE;
@@ -389,16 +430,14 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
         *      of EAP-TLS in order to calculate fragment sizes is
         *      just too much.
         */
-       state->offset = conf->fragment_size;
-       vp = pairfind(request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
-       if (vp && (vp->vp_integer > 100) && (vp->vp_integer < state->offset)) {
-               state->offset = vp->vp_integer;
+       state->mtu = conf->fragment_size;
+       vp = fr_pair_find_by_num(request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
+       if (vp && (vp->vp_integer > 100) && (vp->vp_integer < state->mtu)) {
+               state->mtu = vp->vp_integer;
        }
 
        if (conf->session_cache_enable) state->allow_session_resumption = true; /* otherwise it's false */
 
-       RDEBUG2("Initiate");
-
        return state;
 }
 
@@ -413,23 +452,23 @@ static int int_ssl_check(REQUEST *request, SSL *s, int ret, char const *text)
        if ((l = ERR_get_error()) != 0) {
                char const *p = ERR_error_string(l, NULL);
 
-               if (request && p) REDEBUG("SSL says: %s", p);
+               if (p) ROPTIONAL(REDEBUG, ERROR, "SSL says: %s", p);
        }
-       e = SSL_get_error(s, ret);
 
+       e = SSL_get_error(s, ret);
        switch (e) {
-               /*
-                *      These seem to be harmless and already "dealt
-                *      with" by our non-blocking environment. NB:
-                *      "ZERO_RETURN" is the clean "error"
-                *      indicating a successfully closed SSL
-                *      tunnel. We let this happen because our IO
-                *      loop should not appear to have broken on
-                *      this condition - and outside the IO loop, the
-                *      "shutdown" state is checked.
-                *
-                *      Don't print anything if we ignore the error.
-                */
+       /*
+        *      These seem to be harmless and already "dealt
+        *      with" by our non-blocking environment. NB:
+        *      "ZERO_RETURN" is the clean "error"
+        *      indicating a successfully closed SSL
+        *      tunnel. We let this happen because our IO
+        *      loop should not appear to have broken on
+        *      this condition - and outside the IO loop, the
+        *      "shutdown" state is checked.
+        *
+        *      Don't print anything if we ignore the error.
+        */
        case SSL_ERROR_NONE:
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
@@ -437,29 +476,27 @@ static int int_ssl_check(REQUEST *request, SSL *s, int ret, char const *text)
        case SSL_ERROR_ZERO_RETURN:
                break;
 
-               /*
-                *      These seem to be indications of a genuine
-                *      error that should result in the SSL tunnel
-                *      being regarded as "dead".
-                */
+       /*
+        *      These seem to be indications of a genuine
+        *      error that should result in the SSL tunnel
+        *      being regarded as "dead".
+        */
        case SSL_ERROR_SYSCALL:
-               ERROR("SSL: %s failed in a system call (%d), TLS session fails.",
-                      text, ret);
+               ROPTIONAL(REDEBUG, ERROR, "%s failed in a system call (%d), TLS session failed", text, ret);
                return 0;
 
        case SSL_ERROR_SSL:
-               ERROR("SSL: %s failed inside of TLS (%d), TLS session fails.",
-                      text, ret);
+               ROPTIONAL(REDEBUG, ERROR, "%s failed inside of TLS (%d), TLS session failed", text, ret);
                return 0;
 
+       /*
+        *      For any other errors that (a) exist, and (b)
+        *      crop up - we need to interpret what to do with
+        *      them - so "politely inform" the caller that
+        *      the code needs updating here.
+        */
        default:
-               /*
-                *      For any other errors that (a) exist, and (b)
-                *      crop up - we need to interpret what to do with
-                *      them - so "politely inform" the caller that
-                *      the code needs updating here.
-                */
-               ERROR("SSL: FATAL SSL error ..... %d\n", e);
+               ROPTIONAL(REDEBUG, ERROR, "FATAL SSL error: %d", e);
                return 0;
        }
 
@@ -487,8 +524,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
 
        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,
-                       err);
+               REDEBUG("Failed writing %zd bytes to SSL BIO: %d", ssn->dirty_in.used, err);
                record_init(&ssn->dirty_in);
                return 0;
        }
@@ -501,26 +537,14 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
                return 1;
        }
 
-       if (!int_ssl_check(request, ssn->ssl, err, "SSL_read")) {
-               return 0;
-       }
+       if (!int_ssl_check(request, ssn->ssl, err, "SSL_read")) return 0;
 
        /* Some Extra STATE information for easy debugging */
-       if (SSL_is_init_finished(ssn->ssl)) {
-               DEBUG2("SSL Connection Established\n");
-       }
-       if (SSL_in_init(ssn->ssl)) {
-               DEBUG2("In SSL Handshake Phase\n");
-       }
-       if (SSL_in_before(ssn->ssl)) {
-               DEBUG2("Before SSL Handshake Phase\n");
-       }
-       if (SSL_in_accept_init(ssn->ssl)) {
-               DEBUG2("In SSL Accept mode \n");
-       }
-       if (SSL_in_connect_init(ssn->ssl)) {
-               DEBUG2("In SSL Connect mode \n");
-       }
+       if (SSL_is_init_finished(ssn->ssl)) RDEBUG2("SSL Connection Established");
+       if (SSL_in_init(ssn->ssl)) RDEBUG2("In SSL Handshake Phase");
+       if (SSL_in_before(ssn->ssl)) RDEBUG2("Before SSL Handshake Phase");
+       if (SSL_in_accept_init(ssn->ssl)) RDEBUG2("In SSL Accept mode");
+       if (SSL_in_connect_init(ssn->ssl)) RDEBUG2("In SSL Connect mode");
 
        err = BIO_ctrl_pending(ssn->from_ssl);
        if (err > 0) {
@@ -531,7 +555,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
 
                } else if (BIO_should_retry(ssn->from_ssl)) {
                        record_init(&ssn->dirty_in);
-                       DEBUG2("  tls: Asking for more data in tunnel");
+                       RDEBUG2("Asking for more data in tunnel");
                        return 1;
 
                } else {
@@ -540,7 +564,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
                        return 0;
                }
        } else {
-               DEBUG2("SSL Application Data");
+               RDEBUG2("SSL Application Data");
                /* Its clean application data, do whatever we want */
                record_init(&ssn->clean_out);
        }
@@ -586,7 +610,7 @@ int tls_handshake_send(REQUEST *request, tls_session_t *ssn)
        return 1;
 }
 
-void session_init(tls_session_t *ssn)
+static void session_init(tls_session_t *ssn)
 {
        ssn->ssl = NULL;
        ssn->into_ssl = ssn->from_ssl = NULL;
@@ -597,15 +621,15 @@ void session_init(tls_session_t *ssn)
 
        memset(&ssn->info, 0, sizeof(ssn->info));
 
-       ssn->offset = 0;
-       ssn->fragment = 0;
+       ssn->mtu = 0;
+       ssn->fragment = false;
        ssn->tls_msg_len = 0;
-       ssn->length_flag = 0;
+       ssn->length_flag = false;
        ssn->opaque = NULL;
        ssn->free_opaque = NULL;
 }
 
-void session_close(tls_session_t *ssn)
+static void session_close(tls_session_t *ssn)
 {
        SSL_set_quiet_shutdown(ssn->ssl, 1);
        SSL_shutdown(ssn->ssl);
@@ -680,12 +704,13 @@ void tls_session_information(tls_session_t *tls_session)
        char const *str_write_p, *str_version, *str_content_type = "";
        char const *str_details1 = "", *str_details2= "";
        REQUEST *request;
+       char buffer[32];
 
        /*
         *      Don't print this out in the normal course of
         *      operations.
         */
-       if (debug_flag == 0) {
+       if (rad_debug_lvl == 0) {
                return;
        }
 
@@ -693,7 +718,7 @@ void tls_session_information(tls_session_t *tls_session)
 
        switch (tls_session->info.version) {
        case SSL2_VERSION:
-               str_version = "SSL 2.0";
+               str_version = "SSL 2.0 ";
                break;
        case SSL3_VERSION:
                str_version = "SSL 3.0 ";
@@ -701,8 +726,25 @@ void tls_session_information(tls_session_t *tls_session)
        case TLS1_VERSION:
                str_version = "TLS 1.0 ";
                break;
+#ifdef TLS1_1_VERSION
+       case TLS1_1_VERSION:
+               str_version = "TLS 1.1 ";
+               break;
+#endif
+#ifdef TLS1_2_VERSION
+       case TLS1_2_VERSION:
+               str_version = "TLS 1.2 ";
+               break;
+#endif
+#ifdef TLS1_3_VERSON
+       case TLS1_3_VERSION:
+               str_version = "TLS 1.3 ";
+               break;
+#endif
+
        default:
-               str_version = "Unknown TLS version";
+               sprintf(buffer, "UNKNOWN TLS VERSION ?%04X?", tls_session->info.version);
+               str_version = buffer;
                break;
        }
 
@@ -905,17 +947,19 @@ void tls_session_information(tls_session_t *tls_session)
 
 static CONF_PARSER cache_config[] = {
        { "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 },
+
+       { "max_entries", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, session_cache_size), "255" },
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static CONF_PARSER verify_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 #ifdef HAVE_OPENSSL_OCSP_H
@@ -926,7 +970,7 @@ static CONF_PARSER ocsp_config[] = {
        { "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), "no" },
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 #endif
 
@@ -954,6 +998,9 @@ static CONF_PARSER tls_server_config[] = {
        { "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" },
+#ifdef X509_V_FLAG_CRL_CHECK_ALL
+       { "check_all_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, check_all_crl), "no" },
+#endif
        { "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 },
@@ -966,8 +1013,17 @@ static CONF_PARSER tls_server_config[] = {
 #endif
 #endif
 
+#ifdef SSL_OP_NO_TLSv1
+       { "disable_tlsv1", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disable_tlsv1), NULL },
+#endif
+
+#ifdef SSL_OP_NO_TLSv1_1
        { "disable_tlsv1_1", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disable_tlsv1_1), NULL },
+#endif
+
+#ifdef SSL_OP_NO_TLSv1_2
        { "disable_tlsv1_2", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disable_tlsv1_2), NULL },
+#endif
 
        { "cache", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) cache_config },
 
@@ -976,8 +1032,7 @@ static CONF_PARSER tls_server_config[] = {
 #ifdef HAVE_OPENSSL_OCSP_H
        { "ocsp", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) ocsp_config },
 #endif
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -1008,14 +1063,18 @@ static CONF_PARSER tls_client_config[] = {
 #endif
 #endif
 
+#ifdef SSL_OP_NO_TLSv1
+       { "disable_tlsv1", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disable_tlsv1), NULL },
+#endif
+
 #ifdef SSL_OP_NO_TLSv1_1
        { "disable_tlsv1_1", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disable_tlsv1_1), NULL },
 #endif
+
 #ifdef SSL_OP_NO_TLSv1_2
        { "disable_tlsv1_2", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disable_tlsv1_2), NULL },
 #endif
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -1030,20 +1089,20 @@ static int load_dh_params(SSL_CTX *ctx, char *file)
        if (!file) return 0;
 
        if ((bio = BIO_new_file(file, "r")) == NULL) {
-               ERROR("tls: Unable to open DH file - %s", file);
+               ERROR(LOG_PREFIX ": Unable to open DH file - %s", file);
                return -1;
        }
 
        dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
        BIO_free(bio);
        if (!dh) {
-               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");
+               WARN(LOG_PREFIX ": Unable to set DH parameters.  DH cipher suites may not work!");
+               WARN(LOG_PREFIX ": Fix this by running the OpenSSL command listed in eap.conf");
                return 0;
        }
 
        if (SSL_CTX_set_tmp_dh(ctx, dh) < 0) {
-               ERROR("tls: Unable to set DH parameters");
+               ERROR(LOG_PREFIX ": Unable to set DH parameters");
                DH_free(dh);
                return -1;
        }
@@ -1055,36 +1114,39 @@ static int load_dh_params(SSL_CTX *ctx, char *file)
 
 /*
  *     Print debugging messages, and free data.
- *
- *     FIXME: Write sessions to some long-term storage, so that
- *            session resumption can still occur after the server
- *            restarts.
  */
 #define MAX_SESSION_SIZE (256)
 
 static void cbtls_remove_session(SSL_CTX *ctx, SSL_SESSION *sess)
 {
-       size_t size;
-       char buffer[2 * MAX_SESSION_SIZE + 1];
-       fr_tls_server_conf_t *conf;
+       size_t                  size;
+       char                    buffer[2 * MAX_SESSION_SIZE + 1];
+       fr_tls_server_conf_t    *conf;
 
        size = sess->session_id_length;
        if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
        fr_bin2hex(buffer, sess->session_id, size);
 
-       DEBUG2("  SSL: Removing session %s from the cache", buffer);
        conf = (fr_tls_server_conf_t *)SSL_CTX_get_app_data(ctx);
-       if (conf && conf->session_cache_path) {
+       if (!conf) {
+               DEBUG(LOG_PREFIX ": Failed to find TLS configuration in session");
+               return;
+       }
+
+       {
                int rv;
                char filename[256];
 
+               DEBUG2(LOG_PREFIX ": Removing session %s from the cache", buffer);
+
                /* remove session and any cached VPs */
                snprintf(filename, sizeof(filename), "%s%c%s.asn1",
                         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, fr_syserror(errno));
+                       DEBUG2(LOG_PREFIX ": 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",
@@ -1097,29 +1159,36 @@ static void cbtls_remove_session(SSL_CTX *ctx, SSL_SESSION *sess)
 
 static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
 {
-       size_t size;
-       char buffer[2 * MAX_SESSION_SIZE + 1];
-       fr_tls_server_conf_t *conf;
-       unsigned char *sess_blob = NULL;
+       size_t                  size;
+       char                    buffer[2 * MAX_SESSION_SIZE + 1];
+       fr_tls_server_conf_t    *conf;
+       unsigned char           *sess_blob = NULL;
+
+       REQUEST                 *request = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
+
+       conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF);
+       if (!conf) {
+               RWDEBUG("Failed to find TLS configuration in session");
+               return 0;
+       }
 
        size = sess->session_id_length;
        if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
        fr_bin2hex(buffer, sess->session_id, size);
 
-       DEBUG2("  SSL: adding session %s to cache", buffer);
-
-       conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF);
-       if (conf && conf->session_cache_path) {
+       {
                int fd, rv, todo, blob_len;
                char filename[256];
                unsigned char *p;
 
+               RDEBUG2("Serialising session %s, and storing in cache", buffer);
+
                /* find out what length data we need */
                blob_len = i2d_SSL_SESSION(sess, NULL);
                if (blob_len < 1) {
                        /* something went wrong */
-                       DEBUG2("  SSL: could not find buffer length to persist session");
+                       RWDEBUG("Session serialisation failed, couldn't determine required buffer length");
                        return 0;
                }
 
@@ -1128,14 +1197,14 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
                /* alloc and convert to ASN.1 */
                sess_blob = malloc(blob_len);
                if (!sess_blob) {
-                       DEBUG2("  SSL: could not allocate buffer len=%d to persist session", blob_len);
+                       RWDEBUG("Session serialisation failed, couldn't allocate buffer (%d bytes)", blob_len);
                        return 0;
                }
                /* openssl mutates &p */
                p = sess_blob;
                rv = i2d_SSL_SESSION(sess, &p);
                if (rv != blob_len) {
-                       DEBUG2("  SSL: could not persist session");
+                       RWDEBUG("Session serialisation failed");
                        goto error;
                }
 
@@ -1144,7 +1213,8 @@ 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, fr_syserror(errno));
+                       RERROR("Session serialisation failed, failed opening session file %s: %s",
+                             filename, fr_syserror(errno));
                        goto error;
                }
 
@@ -1153,7 +1223,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", fr_syserror(errno));
+                               RWDEBUG("Failed writing session: %s", fr_syserror(errno));
                                close(fd);
                                goto error;
                        }
@@ -1161,7 +1231,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
                        todo -= rv;
                }
                close(fd);
-               DEBUG2("  SSL: wrote session %s to %s len=%d", buffer, filename, blob_len);
+               RWDEBUG("Wrote session %s to %s (%d bytes)", buffer, filename, blob_len);
        }
 
 error:
@@ -1170,64 +1240,73 @@ error:
        return 0;
 }
 
-static SSL_SESSION *cbtls_get_session(SSL *ssl,
-                                     unsigned char *data, int len,
-                                     int *copy)
+static SSL_SESSION *cbtls_get_session(SSL *ssl, unsigned char *data, int len, int *copy)
 {
-       size_t size;
-       char buffer[2 * MAX_SESSION_SIZE + 1];
-       fr_tls_server_conf_t *conf;
-       TALLOC_CTX *talloc_ctx;
+       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;
-       PAIR_LIST *pairlist = NULL;
+       SSL_SESSION             *sess = NULL;
+       unsigned char           *sess_data = NULL;
+       PAIR_LIST               *pairlist = NULL;
+
+       REQUEST                 *request = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
+
+       rad_assert(request != NULL);
 
        size = len;
        if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
 
        fr_bin2hex(buffer, data, size);
 
-       DEBUG2("  SSL: Client requested cached session %s", buffer);
+       RDEBUG2("Peer requested cached session: %s", buffer);
+
+       *copy = 0;
 
        conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF);
+       if (!conf) {
+               RWDEBUG("Failed to find TLS configuration in session");
+               return NULL;
+       }
+
        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];
-               unsigned char *p;
-               struct stat st;
-               VALUE_PAIR *vp;
+
+       {
+               int             rv, fd, todo;
+               char            filename[256];
+               unsigned char   *p;
+               struct stat     st;
+               VALUE_PAIR      *vps = NULL;
 
                /* read in the cached VPs from the .vps file */
                snprintf(filename, sizeof(filename), "%s%c%s.vps",
                         conf->session_cache_path, FR_DIR_SEP, buffer);
-               rv = pairlist_read(NULL, filename, &pairlist, 1);
+               rv = pairlist_read(talloc_ctx, filename, &pairlist, 1);
                if (rv < 0) {
                        /* not safe to un-persist a session w/o VPs */
-                       DEBUG2("  SSL: could not load persisted VPs for session %s", buffer);
+                       RWDEBUG("Failed loading persisted VPs for session %s", buffer);
                        goto err;
                }
 
                /* load the actual SSL session */
-               snprintf(filename, sizeof(filename), "%s%c%s.asn1",
-                        conf->session_cache_path, FR_DIR_SEP, buffer);
+               snprintf(filename, sizeof(filename), "%s%c%s.asn1", 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, fr_syserror(errno));
+                       RWDEBUG("No 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, fr_syserror(errno));
+                       RWDEBUG("Failed stating persisted session file %s: %s", filename, fr_syserror(errno));
                        close(fd);
                        goto err;
                }
 
                sess_data = talloc_array(NULL, unsigned char, st.st_size);
                if (!sess_data) {
-                 DEBUG2("  SSL: could not alloc buffer for persisted session len=%d", (int) st.st_size);
+                       RWDEBUG("Failed allocating buffer for persisted session (%d bytes)", (int) st.st_size);
                        close(fd);
                        goto err;
                }
@@ -1237,7 +1316,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", fr_syserror(errno));
+                               RWDEBUG("Failed reading persisted session: %s", fr_syserror(errno));
                                close(fd);
                                goto err;
                        }
@@ -1251,49 +1330,59 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                sess = d2i_SSL_SESSION(NULL, (unsigned char const **)(void **) &p, st.st_size);
 
                if (!sess) {
-                       DEBUG2("  SSL: OpenSSL failed to load persisted session: %s", ERR_error_string(ERR_get_error(), NULL));
+                       RWDEBUG("Failed loading persisted session: %s", ERR_error_string(ERR_get_error(), NULL));
                        goto err;
                }
 
-               /* cache the VPs into the session */
-               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);
+               /* move the cached VPs into the session */
+               fr_pair_list_mcopy_by_num(talloc_ctx, &vps, &pairlist->reply, 0, 0, TAG_ANY);
+
+               SSL_SESSION_set_ex_data(sess, fr_tls_ex_index_vps, vps);
+               RWDEBUG("Successfully restored session %s", buffer);
+               rdebug_pair_list(L_DBG_LVL_2, request, vps, "reply:");
        }
 err:
        if (sess_data) talloc_free(sess_data);
        if (pairlist) pairlist_free(&pairlist);
 
-       *copy = 0;
        return sess;
 }
 
 #ifdef HAVE_OPENSSL_OCSP_H
-/*
- * This function extracts the OCSP Responder URL
- * from an existing x509 certificate.
+
+/** Extract components of OCSP responser URL from a certificate
+ *
+ * @param[in] cert to extract URL from.
+ * @param[out] host_out Portion of the URL (must be freed with free()).
+ * @param[out] port_out Port portion of the URL (must be freed with free()).
+ * @param[out] path_out Path portion of the URL (must be freed with free()).
+ * @param[out] is_https Whether the responder should be contacted using https.
+ * @return
+ *     - 0 if no valid URL is contained in the certificate.
+ *     - 1 if a URL was found and parsed.
+ *     - -1 if at least one URL was found, but none could be parsed.
  */
-static int ocsp_parse_cert_url(X509 *cert, char **phost, char **pport,
-                              char **ppath, int *pssl)
+static int ocsp_parse_cert_url(X509 *cert, char **host_out, char **port_out,
+                              char **path_out, int *is_https)
 {
-       int i;
+       int                     i;
+       bool                    found_uri = false;
 
-       AUTHORITY_INFO_ACCESS *aia;
-       ACCESS_DESCRIPTION *ad;
+       AUTHORITY_INFO_ACCESS   *aia;
+       ACCESS_DESCRIPTION      *ad;
 
        aia = X509_get_ext_d2i(cert, NID_info_access, NULL, NULL);
 
        for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
                ad = sk_ACCESS_DESCRIPTION_value(aia, i);
-               if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) {
-                       if (ad->location->type == GEN_URI) {
-                         if(OCSP_parse_url((char *) ad->location->d.ia5->data,
-                                                 phost, pport, ppath, pssl))
-                                       return 1;
-                       }
-               }
+               if (OBJ_obj2nid(ad->method) != NID_ad_OCSP) continue;
+               if (ad->location->type != GEN_URI) continue;
+               found_uri = true;
+
+               if (OCSP_parse_url((char *) ad->location->d.ia5->data, host_out,
+                                  port_out, path_out, is_https)) return 1;
        }
-       return 0;
+       return found_uri ? -1 : 0;
 }
 
 /*
@@ -1304,29 +1393,29 @@ static int ocsp_parse_cert_url(X509 *cert, char **phost, char **pport,
 /* Maximum leeway in validity period: default 5 minutes */
 #define MAX_VALIDITY_PERIOD     (5 * 60)
 
-static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
+static int ocsp_check(REQUEST *request, X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
                      fr_tls_server_conf_t *conf)
 {
-       OCSP_CERTID *certid;
-       OCSP_REQUEST *req;
-       OCSP_RESPONSE *resp = NULL;
-       OCSP_BASICRESP *bresp = NULL;
-       char *host = NULL;
-       char *port = NULL;
-       char *path = NULL;
-       char hostheader[1024];
-       int use_ssl = -1;
-       long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
-       BIO *cbio, *bio_out;
-       int ocsp_ok = 0;
-       int status ;
+       OCSP_CERTID     *certid;
+       OCSP_REQUEST    *req;
+       OCSP_RESPONSE   *resp = NULL;
+       OCSP_BASICRESP  *bresp = NULL;
+       char            *host = NULL;
+       char            *port = NULL;
+       char            *path = NULL;
+       char            hostheader[1024];
+       int             use_ssl = -1;
+       long            nsec = MAX_VALIDITY_PERIOD, maxage = -1;
+       BIO             *cbio, *bio_out;
+       int             ocsp_ok = 0;
+       int             status;
        ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
-       int reason;
+       int             reason;
 #if OPENSSL_VERSION_NUMBER >= 0x1000003f
-       OCSP_REQ_CTX *ctx;
-       int rc;
-       struct timeval now;
-       struct timeval when;
+       OCSP_REQ_CTX    *ctx;
+       int             rc;
+       struct timeval  now;
+       struct timeval  when;
 #endif
 
        /*
@@ -1335,9 +1424,7 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
        certid = OCSP_cert_to_id(NULL, client_cert, issuer_cert);
        req = OCSP_REQUEST_new();
        OCSP_request_add0_id(req, certid);
-       if(conf->ocsp_use_nonce) {
-               OCSP_request_add1_nonce(req, NULL, 8);
-       }
+       if (conf->ocsp_use_nonce) OCSP_request_add1_nonce(req, NULL, 8);
 
        /*
         * Send OCSP Request and get OCSP Response
@@ -1347,25 +1434,43 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
        if (conf->ocsp_override_url) {
                char *url;
 
+       use_ocsp_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) {
+                       RWDEBUG("ocsp: Host or port or path missing from configured URL \"%s\".  Not doing OCSP", url);
+                       ocsp_ok = 2;
+                       goto ocsp_skip;
+               }
+       } else {
+               int ret;
 
-       if (!host || !port || !path) {
-               DEBUG2("[ocsp] - Host / port / path missing.  Not doing OCSP");
-               ocsp_ok = 2;
-               goto ocsp_skip;
+               ret = ocsp_parse_cert_url(client_cert, &host, &port, &path, &use_ssl);
+               switch (ret) {
+               case -1:
+                       RWDEBUG("ocsp: Invalid URL in certificate.  Not doing OCSP");
+                       break;
+
+               case 0:
+                       if (conf->ocsp_url) {
+                               RWDEBUG("ocsp: No OCSP URL in certificate, falling back to configured URL");
+                               goto use_ocsp_url;
+                       }
+                       RWDEBUG("ocsp: No OCSP URL in certificate.  Not doing OCSP");
+                       ocsp_ok = 2;
+                       goto ocsp_skip;
+
+               case 1:
+                       break;
+               }
        }
 
-       DEBUG2("[ocsp] --> Responder URL = http://%s:%s%s", host, port, path);
+       RDEBUG2("ocsp: Using responder URL \"http://%s:%s%s\"", host, port, path);
 
        /* Check host and port length are sane, then create Host: HTTP header */
        if ((strlen(host) + strlen(port) + 2) > sizeof(hostheader)) {
-               ERROR("OCSP Host and port too long");
+               RWDEBUG("ocsp: Host and port too long");
                goto ocsp_skip;
        }
        snprintf(hostheader, sizeof(hostheader), "%s:%s", host, port);
@@ -1374,7 +1479,7 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
        cbio = BIO_new_connect(host);
 
        bio_out = NULL;
-       if (debug_flag) {
+       if (rad_debug_lvl) {
                if (default_log.dst == L_DST_STDOUT) {
                        bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
                } else if (default_log.dst == L_DST_STDERR) {
@@ -1389,7 +1494,7 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
        /* Send OCSP request and wait for response */
        resp = OCSP_sendreq_bio(cbio, path, req);
        if (!resp) {
-               ERROR("Couldn't get OCSP response");
+               REDEBUG("ocsp: Couldn't get OCSP response");
                ocsp_ok = 2;
                goto ocsp_end;
        }
@@ -1399,26 +1504,26 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
 
        rc = BIO_do_connect(cbio);
        if ((rc <= 0) && ((!conf->ocsp_timeout) || !BIO_should_retry(cbio))) {
-               ERROR("Couldn't connect to OCSP responder");
+               REDEBUG("ocsp: Couldn't connect to OCSP responder");
                ocsp_ok = 2;
                goto ocsp_end;
        }
 
        ctx = OCSP_sendreq_new(cbio, path, NULL, -1);
        if (!ctx) {
-               ERROR("Couldn't create OCSP request");
+               REDEBUG("ocsp: Couldn't create OCSP request");
                ocsp_ok = 2;
                goto ocsp_end;
        }
 
        if (!OCSP_REQ_CTX_add1_header(ctx, "Host", hostheader)) {
-               ERROR("Couldn't set Host header");
+               REDEBUG("ocsp: Couldn't set Host header");
                ocsp_ok = 2;
                goto ocsp_end;
        }
 
        if (!OCSP_REQ_CTX_set1_req(ctx, req)) {
-               ERROR("Couldn't add data to OCSP request");
+               REDEBUG("ocsp: Couldn't add data to OCSP request");
                ocsp_ok = 2;
                goto ocsp_end;
        }
@@ -1436,7 +1541,7 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
        } while ((rc == -1) && BIO_should_retry(cbio));
 
        if (conf->ocsp_timeout && (rc == -1) && BIO_should_retry(cbio)) {
-               ERROR("OCSP response timed out");
+               REDEBUG("ocsp: Response timed out");
                ocsp_ok = 2;
                goto ocsp_end;
        }
@@ -1444,7 +1549,7 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
        OCSP_REQ_CTX_free(ctx);
 
        if (rc == 0) {
-               ERROR("Couldn't get OCSP response");
+               REDEBUG("ocsp: Couldn't get OCSP response");
                ocsp_ok = 2;
                goto ocsp_end;
        }
@@ -1452,25 +1557,23 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
 
        /* Verify OCSP response status */
        status = OCSP_response_status(resp);
-       DEBUG2("[ocsp] --> Response status: %s",OCSP_response_status_str(status));
-       if(status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
-               ERROR("OCSP response status: %s", OCSP_response_status_str(status));
+       if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+               REDEBUG("ocsp: Response status: %s", OCSP_response_status_str(status));
                goto ocsp_end;
        }
        bresp = OCSP_response_get1_basic(resp);
-       if(conf->ocsp_use_nonce && OCSP_check_nonce(req, bresp)!=1) {
-               ERROR("OCSP response has wrong nonce value");
+       if (conf->ocsp_use_nonce && OCSP_check_nonce(req, bresp)!=1) {
+               REDEBUG("ocsp: Response has wrong nonce value");
                goto ocsp_end;
        }
-       if(OCSP_basic_verify(bresp, NULL, store, 0)!=1){
-               ERROR("Couldn't verify OCSP basic response");
+       if (OCSP_basic_verify(bresp, NULL, store, 0)!=1){
+               REDEBUG("ocsp: Couldn't verify OCSP basic response");
                goto ocsp_end;
        }
 
        /*      Verify OCSP cert status */
-       if(!OCSP_resp_find_status(bresp, certid, &status, &reason,
-                                                     &rev, &thisupd, &nextupd)) {
-               ERROR("No Status found.\n");
+       if (!OCSP_resp_find_status(bresp, certid, &status, &reason, &rev, &thisupd, &nextupd)) {
+               REDEBUG("ocsp: No Status found");
                goto ocsp_end;
        }
 
@@ -1482,7 +1585,6 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
                goto ocsp_end;
        }
 
-
        if (bio_out) {
                BIO_puts(bio_out, "\tThis Update: ");
                ASN1_GENERALIZEDTIME_print(bio_out, thisupd);
@@ -1496,15 +1598,14 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
 
        switch (status) {
        case V_OCSP_CERTSTATUS_GOOD:
-               DEBUG2("[oscp] --> Cert status: good");
+               RDEBUG2("ocsp: Cert status: good");
                ocsp_ok = 1;
                break;
 
        default:
                /* REVOKED / UNKNOWN */
-               DEBUG2("[ocsp] --> Cert status: %s",OCSP_cert_status_str(status));
-               if (reason != -1)
-                       DEBUG2("[ocsp] --> Reason: %s", OCSP_crl_reason_str(reason));
+               REDEBUG("ocsp: Cert status: %s", OCSP_cert_status_str(status));
+               if (reason != -1) REDEBUG("ocsp: Reason: %s", OCSP_crl_reason_str(reason));
 
                if (bio_out) {
                        BIO_puts(bio_out, "\tRevocation Time: ");
@@ -1528,20 +1629,22 @@ ocsp_end:
  ocsp_skip:
        switch (ocsp_ok) {
        case 1:
-               DEBUG2("[ocsp] --> Certificate is valid!");
+               RDEBUG2("ocsp: Certificate is valid");
                break;
+
        case 2:
                if (conf->ocsp_softfail) {
-                       DEBUG2("[ocsp] --> Unable to check certificate; assuming valid");
-                       DEBUG2("[ocsp] --> Warning! This may be insecure");
+                       RWDEBUG("ocsp: Unable to check certificate, assuming it's valid");
+                       RWDEBUG("ocsp: This may be insecure");
                        ocsp_ok = 1;
                } else {
-                       DEBUG2("[ocsp] --> Unable to check certificate; failing!");
+                       REDEBUG("ocsp: Unable to check certificate, failing");
                        ocsp_ok = 0;
                }
                break;
+
        default:
-               DEBUG2("[ocsp] --> Certificate has been expired/revoked!");
+               REDEBUG("ocsp: Certificate has been expired/revoked");
                break;
        }
 
@@ -1553,14 +1656,14 @@ ocsp_end:
  *     For creating certificate attributes.
  */
 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-Dns",    "TLS-Cert-Subject-Alt-Name-Dns" },
-  { "TLS-Client-Cert-Subject-Alt-Name-Upn",    "TLS-Cert-Subject-Alt-Name-Upn" }
+       { "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-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)
@@ -1599,31 +1702,33 @@ static char const *cert_attr_names[8][2] = {
  */
 int cbtls_verify(int ok, X509_STORE_CTX *ctx)
 {
-       char subject[1024]; /* Used for the subject name */
-       char issuer[1024]; /* Used for the issuer name */
-       char attribute[1024];
-       char value[1024];
-       char common_name[1024];
-       char cn_str[1024];
-       char buf[64];
-       X509 *client_cert;
-       X509_CINF *client_inf;
+       char            subject[1024]; /* Used for the subject name */
+       char            issuer[1024]; /* Used for the issuer name */
+       char            attribute[1024];
+       char            value[1024];
+       char            common_name[1024];
+       char            cn_str[1024];
+       char            buf[64];
+       X509            *client_cert;
+       X509_CINF       *client_inf;
        STACK_OF(X509_EXTENSION) *ext_list;
-       SSL *ssl;
-       int err, depth, lookup, loc;
+       SSL             *ssl;
+       int             err, depth, lookup, loc;
        fr_tls_server_conf_t *conf;
-       int my_ok = ok;
-       REQUEST *request;
-       ASN1_INTEGER *sn = NULL;
-       ASN1_TIME *asn_time = NULL;
-       VALUE_PAIR **certs;
+       int             my_ok = ok;
+
+       ASN1_INTEGER    *sn = NULL;
+       ASN1_TIME       *asn_time = NULL;
+       VALUE_PAIR      **certs;
        char **identity;
 #ifdef HAVE_OPENSSL_OCSP_H
-       X509_STORE *ocsp_store = NULL;
-       X509 *issuer_cert;
+       X509_STORE      *ocsp_store = NULL;
+       X509            *issuer_cert;
 #endif
-       VALUE_PAIR *vp;
-       TALLOC_CTX *talloc_ctx;
+       VALUE_PAIR      *vp;
+       TALLOC_CTX      *talloc_ctx;
+
+       REQUEST         *request;
 
        client_cert = X509_STORE_CTX_get_current_cert(ctx);
        err = X509_STORE_CTX_get_error(ctx);
@@ -1662,7 +1767,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        buf[0] = '\0';
        sn = X509_get_serialNumber(client_cert);
 
-       RDEBUG2("TLS Verify adding attributes");
+       RDEBUG2("Creating attributes from certificate OIDs");
        RINDENT();
 
        /*
@@ -1680,8 +1785,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        sprintf(p, "%02x", (unsigned int)sn->data[i]);
                        p += 2;
                }
-               vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SERIAL][lookup], buf, T_OP_SET);
-               rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+               vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_SERIAL][lookup], buf, T_OP_SET);
+               rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
        }
 
 
@@ -1694,8 +1799,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
            (asn_time->length < (int) sizeof(buf))) {
                memcpy(buf, (char*) asn_time->data, asn_time->length);
                buf[asn_time->length] = '\0';
-               vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_EXPIRATION][lookup], buf, T_OP_SET);
-               rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+               vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_EXPIRATION][lookup], buf, T_OP_SET);
+               rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
        }
 
        /*
@@ -1706,16 +1811,16 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                          sizeof(subject));
        subject[sizeof(subject) - 1] = '\0';
        if (certs && identity && (lookup <= 1) && subject[0]) {
-               vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SUBJECT][lookup], subject, T_OP_SET);
-               rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+               vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_SUBJECT][lookup], subject, T_OP_SET);
+               rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
        }
 
        X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer,
                          sizeof(issuer));
        issuer[sizeof(issuer) - 1] = '\0';
        if (certs && identity && (lookup <= 1) && issuer[0]) {
-               vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_ISSUER][lookup], issuer, T_OP_SET);
-               rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+               vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_ISSUER][lookup], issuer, T_OP_SET);
+               rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
        }
 
        /*
@@ -1725,8 +1830,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                  NID_commonName, common_name, sizeof(common_name));
        common_name[sizeof(common_name) - 1] = '\0';
        if (certs && identity && (lookup <= 1) && common_name[0] && subject[0]) {
-               vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_CN][lookup], common_name, T_OP_SET);
-               rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+               vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_CN][lookup], common_name, T_OP_SET);
+               rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
        }
 
        /*
@@ -1746,16 +1851,16 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                switch (name->type) {
 #ifdef GEN_EMAIL
                                case GEN_EMAIL:
-                                       vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_EMAIL][lookup],
+                                       vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_EMAIL][lookup],
                                                      (char *) ASN1_STRING_data(name->d.rfc822Name), T_OP_SET);
-                                       rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+                                       rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
                                        break;
 #endif /* GEN_EMAIL */
 #ifdef GEN_DNS
                                case GEN_DNS:
-                                       vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_DNS][lookup],
+                                       vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_DNS][lookup],
                                                      (char *) ASN1_STRING_data(name->d.dNSName), T_OP_SET);
-                                       rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+                                       rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
                                        break;
 #endif /* GEN_DNS */
 #ifdef GEN_OTHERNAME
@@ -1764,9 +1869,9 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                        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) {
-                                                   vp = pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_UPN][lookup],
+                                                   vp = fr_pair_make(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_UPN][lookup],
                                                                  (char *) ASN1_STRING_data(name->d.otherName->value->value.utf8string), T_OP_SET);
-                                                   rdebug_pair(L_DBG_LVL_2, request, vp, "&request:");
+                                                   rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
                                                break;
                                            } else {
                                                RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)");
@@ -1813,7 +1918,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
         *      Grab the X509 extensions, and create attributes out of them.
         *      For laziness, we re-use the OpenSSL names
         */
-       if (sk_X509_EXTENSION_num(ext_list) > 0) {
+       if (certs && (sk_X509_EXTENSION_num(ext_list) > 0)) {
                int i, len;
                char *p;
                BIO *out;
@@ -1834,22 +1939,29 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
 
                        attribute[16 + len] = '\0';
 
+                       for (p = attribute + 16; *p != '\0'; p++) {
+                               if (*p == ' ') *p = '-';
+                       }
+
                        X509V3_EXT_print(out, ext, 0, 0);
                        len = BIO_read(out, value , sizeof(value) - 1);
                        if (len <= 0) continue;
 
                        value[len] = '\0';
 
-                       /*
-                        *      Mash the OpenSSL name to our name, and
-                        *      create the attribute.
-                        */
-                       for (p = value + 16; *p != '\0'; p++) {
-                               if (*p == ' ') *p = '-';
+                       vp = fr_pair_make(talloc_ctx, certs, attribute, value, T_OP_ADD);
+                       if (!vp) {
+                               RDEBUG3("Skipping %s += '%s'.  Please check that both the "
+                                       "attribute and value are defined in the dictionaries",
+                                       attribute, value);
+                       } else {
+                               /*
+                                *      rdebug_pair_list indents (so pre REXDENT())
+                                */
+                               REXDENT();
+                               rdebug_pair_list(L_DBG_LVL_2, request, vp, NULL);
+                               RINDENT();
                        }
-
-                       vp = pairmake(talloc_ctx, certs, attribute, value, T_OP_ADD);
-                       rdebug_pair_list(L_DBG_LVL_2, request, vp, NULL);
                }
 
                BIO_free_all(out);
@@ -1891,7 +2003,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                 */
                if (conf->check_cert_issuer &&
                    (strcmp(issuer, conf->check_cert_issuer) != 0)) {
-                       AUTH("tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer);
+                       AUTH(LOG_PREFIX ": Certificate issuer (%s) does not match specified value (%s)!",
+                            issuer, conf->check_cert_issuer);
                        my_ok = 0;
                }
 
@@ -1907,7 +2020,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        } else {
                                RDEBUG2("checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str);
                                if (strcmp(cn_str, common_name) != 0) {
-                                       AUTH("tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str);
+                                       AUTH(LOG_PREFIX ": Certificate CN (%s) does not match specified value (%s)!",
+                                            common_name, cn_str);
                                        my_ok = 0;
                                }
                        }
@@ -1915,11 +2029,11 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
 
 #ifdef HAVE_OPENSSL_OCSP_H
                if (my_ok && conf->ocsp_enable){
-                       RDEBUG2("--> Starting OCSP Request");
+                       RDEBUG2("Starting OCSP Request");
                        if (X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert) != 1) {
                                RERROR("Couldn't get issuer_cert for %s", common_name);
                        } else {
-                               my_ok = ocsp_check(ocsp_store, issuer_cert, client_cert, conf);
+                               my_ok = ocsp_check(request, ocsp_store, issuer_cert, client_cert, conf);
                        }
                }
 #endif
@@ -1953,7 +2067,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        }
                        fclose(fp);
 
-                       if (!pairmake_packet("TLS-Client-Cert-Filename",
+                       if (!pair_make_request("TLS-Client-Cert-Filename",
                                             filename, T_OP_SET)) {
                                RDEBUG("Failed creating TLS-Client-Cert-Filename");
 
@@ -1961,10 +2075,10 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        }
 
                        RDEBUG("Verifying client certificate: %s", conf->verify_client_cert_cmd);
-                       if (radius_exec_program(NULL, 0, NULL, request, conf->verify_client_cert_cmd,
+                       if (radius_exec_program(request, NULL, 0, NULL, request, conf->verify_client_cert_cmd,
                                                request->packet->vps,
                                                true, true, EXEC_TIMEOUT) != 0) {
-                               AUTH("tls: Certificate CN (%s) fails external verification!", common_name);
+                               AUTH(LOG_PREFIX ": Certificate CN (%s) fails external verification!", common_name);
                                my_ok = 0;
                        } else {
                                RDEBUG("Client certificate CN %s passed external validation", common_name);
@@ -1978,15 +2092,15 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
 
        } /* depth == 0 */
 
-       if (debug_flag > 0) {
-               RDEBUG2("chain-depth=%d, ", depth);
-               RDEBUG2("error=%d", err);
+       if (RDEBUG_ENABLED3) {
+               RDEBUG3("chain-depth   : %d", depth);
+               RDEBUG3("error         : %d", err);
 
-               if (identity) RDEBUG2("--> User-Name = %s", *identity);
-               RDEBUG2("--> BUF-Name = %s", common_name);
-               RDEBUG2("--> subject = %s", subject);
-               RDEBUG2("--> issuer  = %s", issuer);
-               RDEBUG2("--> verify return:%d", my_ok);
+               if (identity) RDEBUG3("identity      : %s", *identity);
+               RDEBUG3("common name   : %s", common_name);
+               RDEBUG3("subject       : %s", subject);
+               RDEBUG3("issuer        : %s", issuer);
+               RDEBUG3("verify return : %d", my_ok);
        }
        return my_ok;
 }
@@ -2009,8 +2123,8 @@ static X509_STORE *init_revocation_store(fr_tls_server_conf_t *conf)
        /* Load the CAs we trust */
        if (conf->ca_file || conf->ca_path)
                if(!X509_STORE_load_locations(store, conf->ca_file, conf->ca_path)) {
-                       ERROR("tls: X509_STORE error %s", ERR_error_string(ERR_get_error(), NULL));
-                       ERROR("tls: Error reading Trusted root CA list %s",conf->ca_file );
+                       ERROR(LOG_PREFIX ": X509_STORE error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR(LOG_PREFIX ": Error reading Trusted root CA list %s",conf->ca_file );
                        return NULL;
                }
 
@@ -2018,6 +2132,10 @@ static X509_STORE *init_revocation_store(fr_tls_server_conf_t *conf)
        if (conf->check_crl)
                X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK);
 #endif
+#ifdef X509_V_FLAG_CRL_CHECK_ALL
+       if (conf->check_all_crl)
+               X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK_ALL);
+#endif
        return store;
 }
 #endif /* HAVE_OPENSSL_OCSP_H */
@@ -2033,13 +2151,13 @@ static int set_ecdh_curve(SSL_CTX *ctx, char const *ecdh_curve)
 
        nid = OBJ_sn2nid(ecdh_curve);
        if (!nid) {
-               ERROR("Unknown ecdh_curve \"%s\"", ecdh_curve);
+               ERROR(LOG_PREFIX ": Unknown ecdh_curve \"%s\"", ecdh_curve);
                return -1;
        }
 
        ecdh = EC_KEY_new_by_curve_name(nid);
        if (!ecdh) {
-               ERROR("Unable to create new curve \"%s\"", ecdh_curve);
+               ERROR(LOG_PREFIX ": Unable to create new curve \"%s\"", ecdh_curve);
                return -1;
        }
 
@@ -2069,9 +2187,9 @@ static void sess_free_vps(UNUSED void *parent, void *data_ptr,
        VALUE_PAIR *vp = data_ptr;
        if (!vp) return;
 
-       DEBUG2( Freeing cached session VPs");
+       DEBUG2(LOG_PREFIX ": Freeing cached session VPs");
 
-       pairfree(&vp);
+       fr_pair_list_free(&vp);
 }
 
 static void sess_free_certs(UNUSED void *parent, void *data_ptr,
@@ -2081,9 +2199,9 @@ static void sess_free_certs(UNUSED void *parent, void *data_ptr,
        VALUE_PAIR **certs = data_ptr;
        if (!certs) return;
 
-       DEBUG2( Freeing cached session Certificates");
+       DEBUG2(LOG_PREFIX ": Freeing cached session Certificates");
 
-       pairfree(certs);
+       fr_pair_list_free(certs);
 }
 
 /** Add all the default ciphers and message digests reate our context.
@@ -2158,20 +2276,20 @@ void tls_global_cleanup(void)
        CRYPTO_cleanup_all_ex_data();
 }
 
-/*
- *     Create SSL context
+/** Create SSL context
  *
- *     - Load the trusted CAs
- *     - Load the Private key & the certificate
- *     - Set the Context options & Verify options
+ * - Load the trusted CAs
+ * - Load the Private key & the certificate
+ * - Set the Context options & Verify options
  */
 SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
 {
-       SSL_CTX *ctx;
-       X509_STORE *certstore;
-       int verify_mode = SSL_VERIFY_NONE;
-       int ctx_options = 0;
-       int type;
+       SSL_CTX         *ctx;
+       X509_STORE      *certstore;
+       int             verify_mode = SSL_VERIFY_NONE;
+       int             ctx_options = 0;
+       int             ctx_tls_versions = 0;
+       int             type;
 
        /*
         *      SHA256 is in all versions of OpenSSL, but isn't
@@ -2186,8 +2304,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
        if (!ctx) {
                int err;
                while ((err = ERR_get_error())) {
-                       DEBUG("Failed creating SSL context: %s",
-                             ERR_error_string(err, NULL));
+                       ERROR(LOG_PREFIX ": Failed creating SSL context: %s", ERR_error_string(err, NULL));
                        return NULL;
                }
        }
@@ -2225,20 +2342,20 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
                        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);
+                       DEBUG2(LOG_PREFIX ":  Getting private key passphrase using command \"%s\"", cmd);
 
                        FILE* cmd_pipe = popen(cmd, "r");
                        if (!cmd_pipe) {
-                               ERROR("TLS: %s command failed.  Unable to get private_key_password", cmd);
-                               ERROR("Error reading private_key_file %s", conf->private_key_file);
+                               ERROR(LOG_PREFIX ": %s command failed: Unable to get private_key_password", cmd);
+                               ERROR(LOG_PREFIX ": Error reading private_key_file %s", conf->private_key_file);
                                return NULL;
                        }
 
                        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);
+                               ERROR(LOG_PREFIX ": Can't allocate space for private_key_password");
+                               ERROR(LOG_PREFIX ": Error reading private_key_file %s", conf->private_key_file);
                                pclose(cmd_pipe);
                                return NULL;
                        }
@@ -2249,7 +2366,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
                        /* Get rid of newline at end of password. */
                        password[strlen(password) - 1] = '\0';
 
-                       DEBUG3("tls:  Password from command = \"%s\"", password);
+                       DEBUG3(LOG_PREFIX ": Password from command = \"%s\"", password);
                        conf->private_key_password = password;
                }
 #endif
@@ -2270,7 +2387,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
                 *      statically configured identity and password.
                 */
                if (conf->psk_query && !*conf->psk_query) {
-                       ERROR("Invalid PSK Configuration: psk_query cannot be empty");
+                       ERROR(LOG_PREFIX ": Invalid PSK Configuration: psk_query cannot be empty");
                        return NULL;
                }
 
@@ -2282,7 +2399,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
                }
 
        } else if (conf->psk_query) {
-               ERROR("Invalid PSK Configuration: psk_query cannot be used for outgoing connections");
+               ERROR(LOG_PREFIX ": Invalid PSK Configuration: psk_query cannot be used for outgoing connections");
                return NULL;
        }
 
@@ -2293,7 +2410,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
            (!conf->psk_identity && conf->psk_password) ||
            (conf->psk_identity && !*conf->psk_identity) ||
            (conf->psk_password && !*conf->psk_password)) {
-               ERROR("Invalid PSK Configuration: psk_identity or psk_password are empty");
+               ERROR(LOG_PREFIX ": Invalid PSK Configuration: psk_identity or psk_password are empty");
                return NULL;
        }
 
@@ -2304,7 +2421,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
                if (conf->certificate_file ||
                    conf->private_key_password || conf->private_key_file ||
                    conf->ca_file || conf->ca_path) {
-                       ERROR("When PSKs are used, No certificate configuration is permitted");
+                       ERROR(LOG_PREFIX ": When PSKs are used, No certificate configuration is permitted");
                        return NULL;
                }
 
@@ -2315,8 +2432,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
 
                psk_len = strlen(conf->psk_password);
                if (strlen(conf->psk_password) > (2 * PSK_MAX_PSK_LEN)) {
-                       ERROR("psk_hexphrase is too long (max %d)",
-                              PSK_MAX_PSK_LEN);
+                       ERROR(LOG_PREFIX ": psk_hexphrase is too long (max %d)", PSK_MAX_PSK_LEN);
                        return NULL;
                }
 
@@ -2326,7 +2442,7 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
                 */
                hex_len = fr_hex2bin(buffer, sizeof(buffer), conf->psk_password, psk_len);
                if (psk_len != (2 * hex_len)) {
-                       ERROR("psk_hexphrase is not all hex");
+                       ERROR(LOG_PREFIX ": psk_hexphrase is not all hex");
                        return NULL;
                }
 
@@ -2349,16 +2465,15 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
 
        if (type == SSL_FILETYPE_PEM) {
                if (!(SSL_CTX_use_certificate_chain_file(ctx, conf->certificate_file))) {
-                       ERROR("Error reading certificate file %s:%s",
-                              conf->certificate_file,
-                              ERR_error_string(ERR_get_error(), NULL));
+                       ERROR(LOG_PREFIX ": Error reading certificate file %s:%s", conf->certificate_file,
+                             ERR_error_string(ERR_get_error(), NULL));
                        return NULL;
                }
 
        } else if (!(SSL_CTX_use_certificate_file(ctx, conf->certificate_file, type))) {
-               ERROR("Error reading certificate file %s:%s",
-                      conf->certificate_file,
-                      ERR_error_string(ERR_get_error(), NULL));
+               ERROR(LOG_PREFIX ": Error reading certificate file %s:%s",
+                     conf->certificate_file,
+                     ERR_error_string(ERR_get_error(), NULL));
                return NULL;
        }
 
@@ -2366,8 +2481,8 @@ SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client)
 load_ca:
        if (conf->ca_file || conf->ca_path) {
                if (!SSL_CTX_load_verify_locations(ctx, conf->ca_file, conf->ca_path)) {
-                       ERROR("tls: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
-                       ERROR("tls: Error reading Trusted root CA list %s",conf->ca_file );
+                       ERROR(LOG_PREFIX ": SSL error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR(LOG_PREFIX ": Error reading Trusted root CA list %s",conf->ca_file );
                        return NULL;
                }
        }
@@ -2375,9 +2490,9 @@ load_ca:
 
        if (conf->private_key_file) {
                if (!(SSL_CTX_use_PrivateKey_file(ctx, conf->private_key_file, type))) {
-                       ERROR("Failed reading private key file %s:%s",
-                              conf->private_key_file,
-                              ERR_error_string(ERR_get_error(), NULL));
+                       ERROR(LOG_PREFIX ": Failed reading private key file %s:%s",
+                             conf->private_key_file,
+                             ERR_error_string(ERR_get_error(), NULL));
                        return NULL;
                }
 
@@ -2385,7 +2500,7 @@ load_ca:
                 * Check if the loaded private key is the right one
                 */
                if (!SSL_CTX_check_private_key(ctx)) {
-                       ERROR("Private key does not match the certificate public key");
+                       ERROR(LOG_PREFIX ": Private key does not match the certificate public key");
                        return NULL;
                }
        }
@@ -2404,15 +2519,29 @@ post_ca:
         *      As of 3.0.5, we always allow TLSv1.1 and TLSv1.2.
         *      Though they can be *globally* disabled if necessary.x
         */
+#ifdef SSL_OP_NO_TLSv1
+       if (conf->disable_tlsv1) ctx_options |= SSL_OP_NO_TLSv1;
+
+       ctx_tls_versions |= SSL_OP_NO_TLSv1;
+#endif
 #ifdef SSL_OP_NO_TLSv1_1
        if (conf->disable_tlsv1_1) ctx_options |= SSL_OP_NO_TLSv1_1;
+
+       ctx_tls_versions |= SSL_OP_NO_TLSv1_1;
 #endif
 #ifdef SSL_OP_NO_TLSv1_2
        if (conf->disable_tlsv1_2) ctx_options |= SSL_OP_NO_TLSv1_2;
+
+       ctx_tls_versions |= SSL_OP_NO_TLSv1_2;
 #endif
 
+       if ((ctx_options & ctx_tls_versions) == ctx_tls_versions) {
+               ERROR(LOG_PREFIX ": You have disabled all available TLS versions.  EAP will not work");
+               return NULL;
+       }
+
 #ifdef SSL_OP_NO_TICKET
-       ctx_options |= SSL_OP_NO_TICKET ;
+       ctx_options |= SSL_OP_NO_TICKET;
 #endif
 
        /*
@@ -2468,9 +2597,14 @@ post_ca:
         *      Callbacks, etc. for session resumption.
         */
        if (conf->session_cache_enable) {
-               SSL_CTX_sess_set_new_cb(ctx, cbtls_new_session);
-               SSL_CTX_sess_set_get_cb(ctx, cbtls_get_session);
-               SSL_CTX_sess_set_remove_cb(ctx, cbtls_remove_session);
+               /*
+                *      Cache sessions on disk if requested.
+                */
+               if (conf->session_cache_path) {
+                       SSL_CTX_sess_set_new_cb(ctx, cbtls_new_session);
+                       SSL_CTX_sess_set_get_cb(ctx, cbtls_get_session);
+                       SSL_CTX_sess_set_remove_cb(ctx, cbtls_remove_session);
+               }
 
                SSL_CTX_set_quiet_shutdown(ctx, 1);
                if (fr_tls_ex_index_vps < 0)
@@ -2484,11 +2618,16 @@ post_ca:
        if (conf->check_crl) {
                certstore = SSL_CTX_get_cert_store(ctx);
                if (certstore == NULL) {
-                       ERROR("tls: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
-                       ERROR("tls: Error reading Certificate Store");
+                       ERROR(LOG_PREFIX ": SSL error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR(LOG_PREFIX ": Error reading Certificate Store");
                        return NULL;
                }
                X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
+
+#ifdef X509_V_FLAG_CRL_CHECK_ALL
+               if (conf->check_all_crl)
+                       X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK_ALL);
+#endif
        }
 #endif
 
@@ -2508,8 +2647,8 @@ post_ca:
        /* Load randomness */
        if (conf->random_file) {
                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");
+                       ERROR(LOG_PREFIX ": SSL error %s", ERR_error_string(ERR_get_error(), NULL));
+                       ERROR(LOG_PREFIX ": Error loading randomness");
                        return NULL;
                }
        }
@@ -2519,7 +2658,7 @@ post_ca:
         */
        if (conf->cipher_list) {
                if (!SSL_CTX_set_cipher_list(ctx, conf->cipher_list)) {
-                       ERROR("tls: Error setting cipher list");
+                       ERROR(LOG_PREFIX ": Error setting cipher list");
                        return NULL;
                }
        }
@@ -2532,13 +2671,10 @@ post_ca:
                 *      Create a unique context Id per EAP-TLS configuration.
                 */
                if (conf->session_id_name) {
-                       snprintf(conf->session_context_id,
-                                sizeof(conf->session_context_id),
-                                "FR eap %s",
-                                conf->session_id_name);
+                       snprintf(conf->session_context_id, sizeof(conf->session_context_id),
+                                "FR eap %s", conf->session_id_name);
                } else {
-                       snprintf(conf->session_context_id,
-                                sizeof(conf->session_context_id),
+                       snprintf(conf->session_context_id, sizeof(conf->session_context_id),
                                 "FR eap %p", conf);
                }
 
@@ -2597,7 +2733,7 @@ static fr_tls_server_conf_t *tls_server_conf_alloc(TALLOC_CTX *ctx)
 
        conf = talloc_zero(ctx, fr_tls_server_conf_t);
        if (!conf) {
-               ERROR("Out of memory");
+               ERROR(LOG_PREFIX ": Out of memory");
                return NULL;
        }
 
@@ -2616,7 +2752,7 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
         */
        conf = cf_data_find(cs, "tls-conf");
        if (conf) {
-               DEBUG("Using cached TLS configuration from previous invocation");
+               DEBUG(LOG_PREFIX ": Using cached TLS configuration from previous invocation");
                return conf;
        }
 
@@ -2634,12 +2770,12 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
        if (conf->fragment_size < 100) conf->fragment_size = 100;
 
        if (!conf->private_key_file) {
-               ERROR("TLS Server requires a private key file");
+               ERROR(LOG_PREFIX ": TLS Server requires a private key file");
                goto error;
        }
 
        if (!conf->certificate_file) {
-               ERROR("TLS Server requires a certificate file");
+               ERROR(LOG_PREFIX ": TLS Server requires a certificate file");
                goto error;
        }
 
@@ -2671,13 +2807,14 @@ 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, fr_syserror(errno));
+                       ERROR(LOG_PREFIX ": Failed changing permissions on %s: %s",
+                             conf->verify_tmp_dir, fr_syserror(errno));
                        goto error;
                }
        }
 
        if (conf->verify_client_cert_cmd && !conf->verify_tmp_dir) {
-               ERROR("You MUST set the verify directory in order to use verify_client_cmd");
+               ERROR(LOG_PREFIX ": You MUST set the verify directory in order to use verify_client_cmd");
                goto error;
        }
 
@@ -2695,7 +2832,7 @@ fr_tls_server_conf_t *tls_client_conf_parse(CONF_SECTION *cs)
 
        conf = cf_data_find(cs, "tls-conf");
        if (conf) {
-               DEBUG("Using cached TLS configuration from previous invocation");
+               DEBUG2(LOG_PREFIX ": Using cached TLS configuration from previous invocation");
                return conf;
        }
 
@@ -2755,7 +2892,7 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
         *      user.
         */
        if ((!ssn->allow_session_resumption) ||
-           (((vp = pairfind(request->config_items, PW_ALLOW_SESSION_RESUMPTION, 0, TAG_ANY)) != NULL) &&
+           (((vp = fr_pair_find_by_num(request->config, PW_ALLOW_SESSION_RESUMPTION, 0, TAG_ANY)) != NULL) &&
             (vp->vp_integer == 0))) {
                SSL_CTX_remove_session(ssn->ctx,
                                       ssn->ssl->session);
@@ -2766,9 +2903,10 @@ 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("Forcibly stopping session resumption as it is not allowed");
                        return -1;
                }
+
        /*
         *      Else resumption IS allowed, so we store the
         *      user data in the cache.
@@ -2783,20 +2921,20 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
 
                fr_bin2hex(buffer, ssn->ssl->session->session_id, size);
 
-               vp = paircopy_by_num(talloc_ctx, request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
-               if (vp) pairadd(&vps, vp);
+               vp = fr_pair_list_copy_by_num(talloc_ctx, request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+               if (vp) fr_pair_add(&vps, vp);
 
-               vp = paircopy_by_num(talloc_ctx, request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
-               if (vp) pairadd(&vps, vp);
+               vp = fr_pair_list_copy_by_num(talloc_ctx, request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
+               if (vp) fr_pair_add(&vps, vp);
 
-               vp = paircopy_by_num(talloc_ctx, request->packet->vps, PW_STRIPPED_USER_DOMAIN, 0, TAG_ANY);
-               if (vp) pairadd(&vps, vp);
+               vp = fr_pair_list_copy_by_num(talloc_ctx, request->packet->vps, PW_STRIPPED_USER_DOMAIN, 0, TAG_ANY);
+               if (vp) fr_pair_add(&vps, vp);
 
-               vp = paircopy_by_num(talloc_ctx, request->reply->vps, PW_CHARGEABLE_USER_IDENTITY, 0, TAG_ANY);
-               if (vp) pairadd(&vps, vp);
+               vp = fr_pair_list_copy_by_num(talloc_ctx, request->reply->vps, PW_CHARGEABLE_USER_IDENTITY, 0, TAG_ANY);
+               if (vp) fr_pair_add(&vps, vp);
 
-               vp = paircopy_by_num(talloc_ctx, request->reply->vps, PW_CACHED_SESSION_POLICY, 0, TAG_ANY);
-               if (vp) pairadd(&vps, vp);
+               vp = fr_pair_list_copy_by_num(talloc_ctx, request->reply->vps, PW_CACHED_SESSION_POLICY, 0, TAG_ANY);
+               if (vp) fr_pair_add(&vps, vp);
 
                certs = (VALUE_PAIR **)SSL_get_ex_data(ssn->ssl, fr_tls_ex_index_certs);
 
@@ -2808,36 +2946,62 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                         *      @todo: some go into reply, others into
                         *      request
                         */
-                       pairadd(&vps, paircopy(talloc_ctx, *certs));
+                       fr_pair_add(&vps, fr_pair_list_copy(talloc_ctx, *certs));
+
+                       /*
+                        *      Save the certs in the packet, so that we can see them.
+                        */
+                       fr_pair_add(&request->packet->vps, fr_pair_list_copy(request->packet, *certs));
                }
 
                if (vps) {
-                       RDEBUG2("Saving session %s vps %p in the cache", buffer, vps);
                        SSL_SESSION_set_ex_data(ssn->ssl->session, fr_tls_ex_index_vps, vps);
+                       rdebug_pair_list(L_DBG_LVL_2, request, vps, "  caching ");
+
                        if (conf->session_cache_path) {
                                /* write the VPs to the cache file */
                                char filename[256], buf[1024];
                                FILE *vp_file;
 
+                               RDEBUG2("Saving session %s in the disk cache", buffer);
+
                                snprintf(filename, sizeof(filename), "%s%c%s.vps", conf->session_cache_path,
                                         FR_DIR_SEP, buffer);
                                vp_file = fopen(filename, "w");
                                if (vp_file == NULL) {
-                                       RDEBUG2("Could not write session VPs to persistent cache: %s",
+                                       RWDEBUG("Could not write session VPs to persistent cache: %s",
                                                fr_syserror(errno));
                                } else {
+                                       VALUE_PAIR *prev = NULL;
                                        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);
+                                       fprintf(vp_file, "%s\n\t", buffer);
+
                                        for (vp = fr_cursor_init(&cursor, &vps);
                                             vp;
                                             vp = fr_cursor_next(&cursor)) {
+                                               /*
+                                                *      Terminate the previous line.
+                                                */
+                                               if (prev) fprintf(vp_file, ",\n\t");
+
+                                               /*
+                                                *      Write this one.
+                                                */
                                                vp_prints(buf, sizeof(buf), vp);
-                                               fprintf(vp_file, "\t%s,\n", buf);
+                                               fputs(buf, vp_file);
+                                               prev = vp;
                                        }
+
+                                       /*
+                                        *      Terminate the final line.
+                                        */
+                                       fprintf(vp_file, "\n");
                                        fclose(vp_file);
                                }
+                       } else {
+                               RDEBUG("Failed to find 'persist_dir' in TLS configuration.  Session will not be cached on disk.");
                        }
                } else {
                        RDEBUG2("No information to cache: session caching will be disabled for session %s", buffer);
@@ -2856,48 +3020,27 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
 
                fr_bin2hex(buffer, ssn->ssl->session->session_id, size);
 
-               vps = SSL_SESSION_get_ex_data(ssn->ssl->session, fr_tls_ex_index_vps);
-               if (!vps) {
-                       RWDEBUG("No information in cached session %s", buffer);
-                       return -1;
-               } else {
-                       vp_cursor_t cursor;
-
-                       RDEBUG("Adding cached attributes for session %s:", buffer);
-                       rdebug_pair_list(L_DBG_LVL_1, request, vps, NULL);
-                       for (vp = fr_cursor_init(&cursor, &vps);
-                            vp;
-                            vp = fr_cursor_next(&cursor)) {
-                               /*
-                                *      TLS-* attrs get added back to
-                                *      the request list.
-                                */
-                               if ((vp->da->vendor == 0) &&
-                                   (vp->da->attr >= PW_TLS_CERT_SERIAL) &&
-                                   (vp->da->attr <= PW_TLS_CLIENT_CERT_SUBJECT_ALT_NAME_UPN)) {
-                                       pairadd(&request->packet->vps, paircopyvp(request->packet, vp));
-                               } else {
-                                       pairadd(&request->reply->vps, paircopyvp(request->reply, vp));
-                               }
-                       }
+               /*
+                *      The "restore VPs from OpenSSL cache" code is
+                *      now in eaptls_process()
+                */
 
-                       if (conf->session_cache_path) {
-                               /* "touch" the cached session/vp file */
-                               char filename[256];
-
-                               snprintf(filename, sizeof(filename), "%s%c%s.asn1",
-                                       conf->session_cache_path, FR_DIR_SEP, buffer);
-                               utime(filename, NULL);
-                               snprintf(filename, sizeof(filename), "%s%c%s.vps",
-                                       conf->session_cache_path, FR_DIR_SEP, buffer);
-                               utime(filename, NULL);
-                       }
+               if (conf->session_cache_path) {
+                       /* "touch" the cached session/vp file */
+                       char filename[256];
 
-                       /*
-                        *      Mark the request as resumed.
-                        */
-                       pairmake_packet("EAP-Session-Resumed", "1", T_OP_SET);
+                       snprintf(filename, sizeof(filename), "%s%c%s.asn1",
+                                conf->session_cache_path, FR_DIR_SEP, buffer);
+                       utime(filename, NULL);
+                       snprintf(filename, sizeof(filename), "%s%c%s.vps",
+                                conf->session_cache_path, FR_DIR_SEP, buffer);
+                       utime(filename, NULL);
                }
+
+               /*
+                *      Mark the request as resumed.
+                */
+               pair_make_request("EAP-Session-Resumed", "1", T_OP_SET);
        }
 
        return 0;
@@ -2912,11 +3055,11 @@ void tls_fail(tls_session_t *ssn)
        SSL_CTX_remove_session(ssn->ctx, ssn->ssl->session);
 }
 
-fr_tls_status_t tls_application_data(tls_session_t *ssn,
-                                    REQUEST *request)
+fr_tls_status_t tls_application_data(tls_session_t *ssn, REQUEST *request)
 
 {
        int err;
+       VALUE_PAIR **certs;
 
        /*
         *      Decrypt the complete record.
@@ -2925,7 +3068,7 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
                        ssn->dirty_in.used);
        if (err != (int) ssn->dirty_in.used) {
                record_init(&ssn->dirty_in);
-               RDEBUG("Failed writing %d to SSL BIO: %d", ssn->dirty_in.used, err);
+               RDEBUG("Failed writing %zd bytes to SSL BIO: %d", ssn->dirty_in.used, err);
                return FR_TLS_FAIL;
        }
 
@@ -2975,6 +3118,14 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
         */
        ssn->clean_out.used = err;
 
+       /*
+        *      Add the certificates to intermediate packets, so that
+        *      the inner tunnel policies can use them.
+        */
+       certs = (VALUE_PAIR **)SSL_get_ex_data(ssn->ssl, fr_tls_ex_index_certs);
+
+       if (certs) fr_pair_add(&request->packet->vps, fr_pair_list_copy(request->packet, *certs));
+
        return FR_TLS_OK;
 }
 
@@ -2987,29 +3138,28 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
  */
 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");
+               REDEBUG("Unexpected ACK received:  No ongoing SSL session");
                return FR_TLS_INVALID;
        }
-       if (ssn->info.initialized == 0) {
-               RDEBUG("No SSL info available. Waiting for more SSL data");
+       if (!ssn->info.initialized) {
+               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");
+               REDEBUG("Unexpected ACK received:  We sent no previous messages");
                return FR_TLS_INVALID;
        }
 
        switch (ssn->info.content_type) {
        case alert:
-               RDEBUG2("ACK alert");
+               RDEBUG2("Peer ACKed our alert");
                return FR_TLS_FAIL;
 
        case handshake:
                if ((ssn->info.handshake_type == handshake_finished) && (ssn->dirty_out.used == 0)) {
-                       RDEBUG2("ACK handshake is finished");
+                       RDEBUG2("Peer ACKed our handshake fragment.  handshake is finished");
 
                        /*
                         *      From now on all the content is
@@ -3020,12 +3170,12 @@ fr_tls_status_t tls_ack_handler(tls_session_t *ssn, REQUEST *request)
                        return FR_TLS_SUCCESS;
                } /* else more data to send */
 
-               RDEBUG2("ACK handshake fragment handler");
+               RDEBUG2("Peer ACKed our handshake fragment");
                /* Fragmentation handler, send next fragment */
                return FR_TLS_REQUEST;
 
        case application_data:
-               RDEBUG2("ACK handshake fragment handler in application data");
+               RDEBUG2("Peer ACKed our application data fragment");
                return FR_TLS_REQUEST;
 
                /*
@@ -3033,11 +3183,9 @@ fr_tls_status_t tls_ack_handler(tls_session_t *ssn, REQUEST *request)
                 *      to the default section below.
                 */
        default:
-               RERROR("Invalid ACK received: %d", ssn->info.content_type);
-
+               REDEBUG("Invalid ACK received: %d", ssn->info.content_type);
                return FR_TLS_INVALID;
        }
 }
-
 #endif /* WITH_TLS */
 
index ba81340..61bc9c8 100644 (file)
@@ -54,7 +54,7 @@ static void dump_hex(char const *msg, uint8_t const *data, size_t data_len)
 {
        size_t i;
 
-       if (debug_flag < 3) return;
+       if (rad_debug_lvl < 3) return;
 
        printf("%s %d\n", msg, (int) data_len);
        if (data_len > 256) data_len = 256;
@@ -139,13 +139,11 @@ static int tls_socket_recv(rad_listen_t *listener)
                sock->packet->dst_ipaddr = sock->my_ipaddr;
                sock->packet->dst_port = sock->my_port;
 
-               if (sock->request) {
-                       sock->request->packet = talloc_steal(sock->request, sock->packet);
-               }
+               if (sock->request) sock->request->packet = talloc_steal(sock->request, sock->packet);
        }
 
        /*
-        *      Allocate a REQUEST for debugging.
+        *      Allocate a REQUEST for debugging, and initialize the TLS session.
         */
        if (!sock->request) {
                sock->request = request = request_alloc(sock);
@@ -161,9 +159,6 @@ static int tls_socket_recv(rad_listen_t *listener)
                request->component = "<core>";
                request->component = "<tls-connect>";
 
-               /*
-                *      Not sure if we should do this on every packet...
-                */
                request->reply = rad_alloc(request, false);
                if (!request->reply) return 0;
 
@@ -178,7 +173,7 @@ static int tls_socket_recv(rad_listen_t *listener)
                }
 
                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_certs, (void *) &sock->certs);
                SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_TALLOC, sock->parent);
 
                doing_init = true;
@@ -227,28 +222,33 @@ static int tls_socket_recv(rad_listen_t *listener)
        }
 
        /*
-        *      Skip ahead to reading application data.
+        *      If we need to do more initialization, do that here.
         */
-       if (SSL_is_init_finished(sock->ssn->ssl)) goto app;
+       if (!SSL_is_init_finished(sock->ssn->ssl)) {
+               if (!tls_handshake_recv(request, sock->ssn)) {
+                       RDEBUG("FAILED in TLS handshake receive");
+                       goto do_close;
+               }
 
-       if (!tls_handshake_recv(request, sock->ssn)) {
-               RDEBUG("FAILED in TLS handshake receive");
-               goto do_close;
-       }
+               /*
+                *      More ACK data to send.  Do so.
+                */
+               if (sock->ssn->dirty_out.used > 0) {
+                       tls_socket_write(listener, request);
+                       PTHREAD_MUTEX_UNLOCK(&sock->mutex);
+                       return 0;
+               }
 
-       if (sock->ssn->dirty_out.used > 0) {
-               tls_socket_write(listener, request);
-               PTHREAD_MUTEX_UNLOCK(&sock->mutex);
-               return 0;
+               /*
+                *      FIXME: Run the request through a virtual
+                *      server in order to see if we like the
+                *      certificate presented by the client.
+                */
        }
 
-app:
        /*
-        *      FIXME: Run the packet through a virtual server in
-        *      order to see if we like the certificate presented by
-        *      the client.
+        *      Try to get application data.
         */
-
        status = tls_application_data(sock->ssn, request);
        RDEBUG("Application data status %d", status);
 
@@ -262,6 +262,9 @@ app:
                return 0;
        }
 
+       /*
+        *      We now have a bunch of application data.
+        */
        dump_hex("TUNNELED DATA > ", sock->ssn->clean_out.data, sock->ssn->clean_out.used);
 
        /*
@@ -270,7 +273,7 @@ app:
         */
        if ((sock->ssn->clean_out.used < 20) ||
            (((sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]) != (int) sock->ssn->clean_out.used)) {
-               RDEBUG("Received bad packet: Length %d contents %d",
+               RDEBUG("Received bad packet: Length %zd contents %d",
                       sock->ssn->clean_out.used,
                       (sock->ssn->clean_out.data[2] << 8) | sock->ssn->clean_out.data[3]);
                goto do_close;
@@ -284,7 +287,7 @@ app:
        PTHREAD_MUTEX_UNLOCK(&sock->mutex);
 
        if (!rad_packet_ok(packet, 0, NULL)) {
-               RDEBUG("Received bad packet: %s", fr_strerror());
+               if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
                DEBUG("Closing TLS socket from client");
                PTHREAD_MUTEX_LOCK(&sock->mutex);
                tls_socket_close(listener);
@@ -295,7 +298,7 @@ app:
        /*
         *      Copied from src/lib/radius.c, rad_recv();
         */
-       if (fr_debug_flag) {
+       if (fr_debug_lvl) {
                char host_ipaddr[128];
 
                if (is_radius_code(packet->code)) {
@@ -345,7 +348,8 @@ int dual_tls_recv(rad_listen_t *listener)
        rad_assert(sock->ssn != NULL);
        rad_assert(client != NULL);
 
-       packet = sock->packet;
+       packet = talloc_steal(NULL, sock->packet);
+       sock->packet = NULL;
 
        /*
         *      Some sanity checks, based on the packet code.
@@ -381,7 +385,7 @@ int dual_tls_recv(rad_listen_t *listener)
                if (!main_config.status_server) {
                        FR_STATS_INC(auth, total_unknown_types);
                        WARN("Ignoring Status-Server request due to security configuration");
-                       rad_free(&sock->packet);
+                       rad_free(&packet);
                        return 0;
                }
                fun = rad_status_server;
@@ -393,18 +397,16 @@ int dual_tls_recv(rad_listen_t *listener)
 
                DEBUG("Invalid packet code %d sent from client %s port %d : IGNORED",
                      packet->code, client->shortname, packet->src_port);
-               rad_free(&sock->packet);
+               rad_free(&packet);
                return 0;
        } /* switch over packet types */
 
        if (!request_receive(NULL, listener, packet, client, fun)) {
                FR_STATS_INC(auth, total_packets_dropped);
-               rad_free(&sock->packet);
+               rad_free(&packet);
                return 0;
        }
 
-       sock->packet = NULL;    /* we have no need for more partial reads */
-
        return 1;
 }
 
@@ -502,7 +504,7 @@ static ssize_t proxy_tls_read(rad_listen_t *listener)
         *      Get the maximum size of data to receive.
         */
        if (!sock->data) sock->data = talloc_array(sock, uint8_t,
-                                                  sock->ssn->offset);
+                                                  sock->ssn->mtu);
 
        data = sock->data;
 
@@ -552,7 +554,7 @@ static ssize_t proxy_tls_read(rad_listen_t *listener)
                 *      FIXME: allocate a RADIUS_PACKET, and set
                 *      "data" to be as large as necessary.
                 */
-               if (length > sock->ssn->offset) {
+               if (length > sock->ssn->mtu) {
                        INFO("Received packet will be too large! Set \"fragment_size = %u\"",
                             (data[2] << 8) | data[3]);
                        goto do_close;
index 6eb2954..ae89b33 100644 (file)
 /**
  * $Id$
  *
- * @brief VALUE_PAIR template functions
+ * @brief #VALUE_PAIR template functions
  * @file main/tmpl.c
  *
  * @ingroup AVP
  *
- * @copyright 2014 The FreeRADIUS server project
+ * @copyright 2014-2015 The FreeRADIUS server project
  */
 RCSID("$Id$")
 
@@ -31,6 +31,8 @@ RCSID("$Id$")
 
 #include <ctype.h>
 
+/** Map #tmpl_type_t values to descriptive strings
+ */
 FR_NAME_NUMBER const tmpl_names[] = {
        { "literal",            TMPL_TYPE_LITERAL       },
        { "xlat",               TMPL_TYPE_XLAT          },
@@ -46,6 +48,8 @@ FR_NAME_NUMBER const tmpl_names[] = {
        { NULL, 0 }
 };
 
+/** Map keywords to #pair_lists_t values
+ */
 const FR_NAME_NUMBER pair_lists[] = {
        { "request",            PAIR_LIST_REQUEST },
        { "reply",              PAIR_LIST_REPLY },
@@ -65,6 +69,8 @@ const FR_NAME_NUMBER pair_lists[] = {
        {  NULL , -1 }
 };
 
+/** Map keywords to #request_refs_t values
+ */
 const FR_NAME_NUMBER request_refs[] = {
        { "outer",              REQUEST_OUTER },
        { "current",            REQUEST_CURRENT },
@@ -72,27 +78,44 @@ const FR_NAME_NUMBER request_refs[] = {
        {  NULL , -1 }
 };
 
-/** Resolve attribute name to a list.
+/** @name Parse list and request qualifiers to #pair_lists_t and #request_refs_t values
+ *
+ * These functions also resolve #pair_lists_t and #request_refs_t values to #REQUEST
+ * structs and the head of #VALUE_PAIR lists in those structs.
+ *
+ * For adding new #VALUE_PAIR to the lists, the #radius_list_ctx function can be used
+ * to obtain the appropriate TALLOC_CTX pointer.
+ *
+ * @note These don't really have much to do with #vp_tmpl_t. They're in the same
+ *     file as they're used almost exclusively by the tmpl_* functions.
+ * @{
+ */
+
+/** Resolve attribute name to a #pair_lists_t value.
  *
- * Check the name string for qualifiers that specify a list and write
- * a pair_lists_t value for that list to out. This value may be passed to
- * radius_list, along with the current request, to get a pointer to the
- * actual list in the request.
+ * Check the name string for #pair_lists qualifiers and write a #pair_lists_t value
+ * for that list to out. This value may be passed to #radius_list, along with the current
+ * #REQUEST, to get a pointer to the actual list in the #REQUEST.
  *
- * If we're sure we've definitely found a list qualifier token delimiter
- * but the string doesn't match a list qualifier, return 0 and write
- * PAIR_LIST_UNKNOWN to out.
+ * If we're sure we've definitely found a list qualifier token delimiter (``:``) but the
+ * string doesn't match a #radius_list qualifier, return 0 and write #PAIR_LIST_UNKNOWN
+ * to out.
  *
- * radius_list_name should be called before passing a name string that
- * may contain qualifiers to dict_attrbyname.
+ * If we can't find a string that looks like a request qualifier, set out to def, and
+ * return 0.
  *
- * @see dict_attrbyname
+ * @note #radius_list_name should be called before passing a name string that may
+ *     contain qualifiers to #dict_attrbyname.
  *
  * @param[out] out Where to write the list qualifier.
  * @param[in] name String containing list qualifiers to parse.
  * @param[in] def the list to return if no qualifiers were found.
- * @return 0 if no valid list qualifier could be found, else the number of
- *     bytes consumed.
+ * @return 0 if no valid list qualifier could be found, else the number of bytes consumed.
+ *     The caller may then advanced the name pointer by the value returned, to get the
+ *     start of the attribute name (if any).
+ *
+ * @see pair_list
+ * @see radius_list
  */
 size_t radius_list_name(pair_lists_t *out, char const *name, pair_lists_t def)
 {
@@ -156,15 +179,18 @@ size_t radius_list_name(pair_lists_t *out, char const *name, pair_lists_t def)
        }
 }
 
-/** Resolve attribute pair_lists_t value to an attribute list.
+/** Resolve attribute #pair_lists_t value to an attribute list.
  *
- * The value returned is a pointer to the pointer of the HEAD of the list
- * in the REQUEST. If the head of the list changes, the pointer will still
- * be valid.
+ * The value returned is a pointer to the pointer of the HEAD of a #VALUE_PAIR list in the
+ * #REQUEST. If the head of the list changes, the pointer will still be valid.
  *
  * @param[in] request containing the target lists.
- * @param[in] list pair_list_t value to resolve to VALUE_PAIR list.
- *     Will be NULL if list name couldn't be resolved.
+ * @param[in] list #pair_lists_t value to resolve to #VALUE_PAIR list. Will be NULL if list
+ *     name couldn't be resolved.
+ * @return a pointer to the HEAD of a list in the #REQUEST.
+ *
+ * @see tmpl_cursor_init
+ * @see fr_cursor_init
  */
 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
 {
@@ -176,13 +202,15 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
                break;
 
        case PAIR_LIST_REQUEST:
+               if (!request->packet) return NULL;
                return &request->packet->vps;
 
        case PAIR_LIST_REPLY:
+               if (!request->reply) return NULL;
                return &request->reply->vps;
 
        case PAIR_LIST_CONTROL:
-               return &request->config_items;
+               return &request->config;
 
        case PAIR_LIST_STATE:
                return &request->state;
@@ -193,7 +221,7 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
                return &request->proxy->vps;
 
        case PAIR_LIST_PROXY_REPLY:
-               if (!request->proxy) break;
+               if (!request->proxy_reply) break;
                return &request->proxy_reply->vps;
 #endif
 #ifdef WITH_COA
@@ -235,16 +263,20 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
        return NULL;
 }
 
-/** Resolve a list name to the packet that parents the vps
+/** Resolve a list to the #RADIUS_PACKET holding the HEAD pointer for a #VALUE_PAIR list
  *
- * Returns the packet for an attribute list.
- * @param[in] request containing the target lists.
- * @param[in] list_name pair_list_t value to resolve to RADIUS_PACKET.
- * @return a RADIUS_PACKET on success, else NULL
+ * Returns a pointer to the #RADIUS_PACKET that holds the HEAD pointer of a given list,
+ * for the current #REQUEST.
+ *
+ * @param[in] request To resolve list in.
+ * @param[in] list #pair_lists_t value to resolve to #RADIUS_PACKET.
+ * @return a #RADIUS_PACKET on success, else NULL.
+ *
+ * @see radius_list
  */
-RADIUS_PACKET *radius_packet(REQUEST *request, pair_lists_t list_name)
+RADIUS_PACKET *radius_packet(REQUEST *request, pair_lists_t list)
 {
-       switch (list_name) {
+       switch (list) {
        /* Don't add default */
        case PAIR_LIST_STATE:
        case PAIR_LIST_CONTROL:
@@ -279,19 +311,24 @@ RADIUS_PACKET *radius_packet(REQUEST *request, pair_lists_t list_name)
        return NULL;
 }
 
-/** Get the correct TALLOC ctx for a list
+/** Return the correct TALLOC_CTX to alloc #VALUE_PAIR in, for a list
  *
- * Returns the talloc context associated with an attribute list.
+ * Allocating new #VALUE_PAIR in the context of a #REQUEST is usually wrong.
+ * #VALUE_PAIR should be allocated in the context of a #RADIUS_PACKET, so that if the
+ * #RADIUS_PACKET is freed before the #REQUEST, the associated #VALUE_PAIR lists are
+ * freed too.
  *
  * @param[in] request containing the target lists.
- * @param[in] list_name pair_list_t value to resolve to TALLOC_CTX.
- * @return a TALLOC_CTX on success, else NULL
+ * @param[in] list #pair_lists_t value to resolve to TALLOC_CTX.
+ * @return a TALLOC_CTX on success, else NULL.
+ *
+ * @see radius_list
  */
-TALLOC_CTX *radius_list_ctx(REQUEST *request, pair_lists_t list_name)
+TALLOC_CTX *radius_list_ctx(REQUEST *request, pair_lists_t list)
 {
        if (!request) return NULL;
 
-       switch (list_name) {
+       switch (list) {
        case PAIR_LIST_REQUEST:
                return request->packet;
 
@@ -345,25 +382,29 @@ TALLOC_CTX *radius_list_ctx(REQUEST *request, pair_lists_t list_name)
        return NULL;
 }
 
-/** Resolve attribute name to a request.
+/** Resolve attribute name to a #request_refs_t value.
  *
- * Check the name string for qualifiers that reference a parent request.
+ * Check the name string for qualifiers that reference a parent #REQUEST.
  *
- * If we find a string that matches a request, then return the number of
- * chars we consumed.
+ * If we find a string that matches a #request_refs qualifier, return the number of chars
+ * we consumed.
  *
- * If we find a string that looks like a request qualifier but isn't,
- * return 0 and set out to REQUEST_UNKNOWN.
+ * If we're sure we've definitely found a list qualifier token delimiter (``*``) but the
+ * qualifier doesn't match one of the #request_refs qualifiers, return 0 and set out to
+ * #REQUEST_UNKNOWN.
  *
- * If we can't find a string that looks like a request qualifier, set out
- * to def, and return 0.
+ * If we can't find a string that looks like a request qualifier, set out to def, and
+ * return 0.
  *
- * @see radius_list_name
- * @param[out] out request ref.
+ * @param[out] out The #request_refs_t value the name resolved to (or #REQUEST_UNKNOWN).
  * @param[in] name of attribute.
  * @param[in] def default request ref to return if no request qualifier is present.
- * @return 0 if no valid request qualifier could be found, else the number of
- *     bytes consumed.
+ * @return 0 if no valid request qualifier could be found, else the number of bytes consumed.
+ *     The caller may then advanced the name pointer by the value returned, to get the
+ *     start of the attribute list or attribute name(if any).
+ *
+ * @see radius_list_name
+ * @see request_refs
  */
 size_t radius_request_name(request_refs_t *out, char const *name, request_refs_t def)
 {
@@ -389,13 +430,16 @@ size_t radius_request_name(request_refs_t *out, char const *name, request_refs_t
        return (q + 1) - p;
 }
 
-/** Resolve request to a request.
+/** Resolve a #request_refs_t to a #REQUEST.
  *
- * Resolve name to a current request.
+ * Sometimes #REQUEST structs may be chained to each other, as is the case
+ * when internally proxying EAP. This function resolves a #request_refs_t
+ * to a #REQUEST higher in the chain than the current #REQUEST.
  *
  * @see radius_list
- * @param[in,out] context Base context to use, and to write the result back to.
- * @param[in] name (request) to resolve to.
+ * @param[in,out] context #REQUEST to start resolving from, and where to write
+ *     a pointer to the resolved #REQUEST back to.
+ * @param[in] name (request) to resolve.
  * @return 0 if request is valid in this context, else -1.
  */
 int radius_request(REQUEST **context, request_refs_t name)
@@ -422,1127 +466,1057 @@ int radius_request(REQUEST **context, request_refs_t name)
 
        return 0;
 }
+/** @} */
 
-#ifdef WITH_VERIFY_PTR
-static uint8_t const *not_zeroed(uint8_t const *ptr, size_t len)
+/** @name Alloc or initialise #vp_tmpl_t
+ *
+ * @note Should not usually be called outside of tmpl_* functions, use one of
+ *     the tmpl_*from_* functions instead.
+ * @{
+ */
+
+/** Initialise stack allocated #vp_tmpl_t
+ *
+ * @note Name is not strdupe'd or memcpy'd so must be available, and must not change
+ *     for the lifetime of the #vp_tmpl_t.
+ *
+ * @param[out] vpt to initialise.
+ * @param[in] type to set in the #vp_tmpl_t.
+ * @param[in] name of the #vp_tmpl_t.
+ * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
+ *     If < 0 strlen will be used to determine the length.
+ * @return a pointer to the initialised #vp_tmpl_t. The same value as
+ *     vpt.
+ */
+vp_tmpl_t *tmpl_init(vp_tmpl_t *vpt, tmpl_type_t type, char const *name, ssize_t len)
 {
-       size_t i;
+       rad_assert(vpt);
+       rad_assert(type != TMPL_TYPE_UNKNOWN);
+       rad_assert(type <= TMPL_TYPE_NULL);
 
-       for (i = 0; i < len; i++) {
-               if (ptr[i] != 0x00) return ptr + i;
-       }
+       memset(vpt, 0, sizeof(vp_tmpl_t));
+       vpt->type = type;
 
-       return NULL;
+       if (name) {
+               vpt->name = name;
+               vpt->len = len < 0 ? strlen(name) :
+                                    (size_t) len;
+       }
+       return vpt;
 }
-#define CHECK_ZEROED(_x) not_zeroed((uint8_t const *)&_x + sizeof(_x), sizeof(vpt->data) - sizeof(_x))
-
 
-/** Verify fields of a value_pair_tmpl_t make sense
+/** Create a new heap allocated #vp_tmpl_t
  *
+ * @param[in,out] ctx to allocate in.
+ * @param[in] type to set in the #vp_tmpl_t.
+ * @param[in] name of the #vp_tmpl_t (will be copied to a new talloc buffer parented
+ *     by the #vp_tmpl_t).
+ * @param[in] len The length of the buffer (or a substring of the buffer) pointed to by name.
+ *     If < 0 strlen will be used to determine the length.
+ * @return the newly allocated #vp_tmpl_t.
  */
-void tmpl_verify(char const *file, int line, value_pair_tmpl_t const *vpt)
+vp_tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name, ssize_t len)
 {
-       if (vpt->type == TMPL_TYPE_UNKNOWN) {
-               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: value_pair_tmpl_t type was "
-                            "TMPL_TYPE_UNKNOWN (uninitialised)", file, line);
-               fr_assert(0);
-               fr_exit_now(1);
+       vp_tmpl_t *vpt;
+
+       rad_assert(type != TMPL_TYPE_UNKNOWN);
+       rad_assert(type <= TMPL_TYPE_NULL);
+
+       vpt = talloc_zero(ctx, vp_tmpl_t);
+       if (!vpt) return NULL;
+       vpt->type = type;
+       if (name) {
+               vpt->name = talloc_bstrndup(vpt, name, len < 0 ? strlen(name) : (size_t)len);
+               vpt->len = talloc_array_length(vpt->name) - 1;
        }
 
-       if (vpt->type > TMPL_TYPE_NULL) {
-               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: value_pair_tmpl_t type was %i "
-                            "(outside range of tmpl_names)", file, line, vpt->type);
-               fr_assert(0);
-               fr_exit_now(1);
+       return vpt;
+}
+/* @} **/
+
+/** @name Create new #vp_tmpl_t from a string
+ *
+ * @{
+ */
+/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
+ *
+ * @note The name field is just a copy of the input pointer, if you know that string might be
+ *     freed before you're done with the #vp_tmpl_t use #tmpl_afrom_attr_str
+ *     instead.
+ *
+ * @param[out] vpt to modify.
+ * @param[in] name of attribute including #request_refs and #pair_lists qualifiers.
+ *     If only #request_refs and #pair_lists qualifiers are found, a #TMPL_TYPE_LIST
+ *     #vp_tmpl_t will be produced.
+ * @param[in] request_def The default #REQUEST to set if no #request_refs qualifiers are
+ *     found in name.
+ * @param[in] list_def The default list to set if no #pair_lists qualifiers are found in
+ *     name.
+ * @param[in] allow_unknown If true attributes in the format accepted by
+ *     #dict_unknown_from_substr will be allowed, even if they're not in the main
+ *     dictionaries.
+ *     If an unknown attribute is found a #TMPL_TYPE_ATTR #vp_tmpl_t will be
+ *     produced with the unknown #DICT_ATTR stored in the ``unknown.da`` buffer.
+ *     This #DICT_ATTR will have its ``flags.is_unknown`` field set to true.
+ *     If #tmpl_from_attr_substr is being called on startup, the #vp_tmpl_t may be
+ *     passed to #tmpl_define_unknown_attr to add the unknown attribute to the main
+ *     dictionary.
+ *     If the unknown attribute is not added to the main dictionary the #vp_tmpl_t
+ *     cannot be used to search for a #VALUE_PAIR in a #REQUEST.
+ * @param[in] allow_undefined If true, we don't generate a parse error on unknown attributes.
+ *     If an unknown attribute is found a #TMPL_TYPE_ATTR_UNDEFINED #vp_tmpl_t
+ *     will be produced.
+ * @return <= 0 on error (offset as negative integer), > 0 on success
+ *     (number of bytes parsed).
+ *
+ * @see REMARKER to produce pretty error markers from the return value.
+ */
+ssize_t tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name,
+                             request_refs_t request_def, pair_lists_t list_def,
+                             bool allow_unknown, bool allow_undefined)
+{
+       char const *p;
+       long num;
+       char *q;
+       tmpl_type_t type = TMPL_TYPE_ATTR;
+
+       value_pair_tmpl_attr_t attr;    /* So we don't fill the tmpl with junk and then error out */
+
+       memset(vpt, 0, sizeof(*vpt));
+       memset(&attr, 0, sizeof(attr));
+
+       p = name;
+
+       if (*p == '&') p++;
+
+       p += radius_request_name(&attr.request, p, request_def);
+       if (attr.request == REQUEST_UNKNOWN) {
+               fr_strerror_printf("Invalid request qualifier");
+               return -(p - name);
        }
 
        /*
-        *  Do a memcmp of the bytes after where the space allocated for
-        *  the union member should have ended and the end of the union.
-        *  These should always be zero if the union has been initialised
-        *  properly.
-        *
-        *  If they're still all zero, do TMPL_TYPE specific checks.
+        *      Finding a list qualifier is optional
         */
-       switch (vpt->type) {
-       case TMPL_TYPE_NULL:
-               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_NULL "
-                                    "has non-zero bytes in its data union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-               break;
+       p += radius_list_name(&attr.list, p, list_def);
+       if (attr.list == PAIR_LIST_UNKNOWN) {
+               fr_strerror_printf("Invalid list qualifier");
+               return -(p - name);
+       }
 
-       case TMPL_TYPE_LITERAL:
-               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LITERAL "
-                                    "has non-zero bytes in its data union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-               break;
+       attr.tag = TAG_ANY;
+       attr.num = NUM_ANY;
 
-       case TMPL_TYPE_XLAT:
-       case TMPL_TYPE_XLAT_STRUCT:
-               break;
+       /*
+        *      This may be just a bare list, but it can still
+        *      have instance selectors and tag selectors.
+        */
+       switch (*p) {
+       case '\0':
+               type = TMPL_TYPE_LIST;
+               attr.num = NUM_ALL;     /* Hack - Should be removed once tests are updated */
+               goto finish;
 
-/* @todo When regexes get converted to xlat the flags field of the regex union is used
-       case TMPL_TYPE_XLAT:
-               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
-                                    "has non-zero bytes in its data union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-               break;
+       case '[':
+               type = TMPL_TYPE_LIST;
+               attr.num = NUM_ALL;     /* Hack - Should be removed once tests are updated */
+               goto do_num;
 
-       case TMPL_TYPE_XLAT_STRUCT:
-               if (CHECK_ZEROED(vpt->data.xlat)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_STRUCT "
-                                    "has non-zero bytes after the data.xlat pointer in the union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+       default:
                break;
-*/
+       }
 
-       case TMPL_TYPE_EXEC:
-               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_EXEC "
-                                    "has non-zero bytes in its data union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-               break;
+       attr.da = dict_attrbyname_substr(&p);
+       if (!attr.da) {
+               char const *a;
 
-       case TMPL_TYPE_ATTR_UNDEFINED:
-               rad_assert(vpt->tmpl_da == NULL);
-               break;
+               /*
+                *      Record start of attribute in case we need to error out.
+                */
+               a = p;
 
-       case TMPL_TYPE_ATTR:
-               if (CHECK_ZEROED(vpt->data.attribute)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                    "has non-zero bytes after the data.attribute struct in the union",
-                                    file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+               fr_strerror();  /* Clear out any existing errors */
 
-               if (vpt->tmpl_da->flags.is_unknown) {
-                       if (vpt->tmpl_da != (DICT_ATTR *)&vpt->data.attribute.unknown.da) {
-                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                            "da is marked as unknown, but does not point to the template's "
-                                            "unknown da buffer", file, line);
-                               fr_assert(0);
-                               fr_exit_now(1);
+               /*
+                *      Attr-1.2.3.4 is OK.
+                */
+               if (dict_unknown_from_substr((DICT_ATTR *)&attr.unknown.da, &p) == 0) {
+                       /*
+                        *      Check what we just parsed really hasn't been defined
+                        *      in the main dictionaries.
+                        *
+                        *      If it has, parsing is the same as if the attribute
+                        *      name had been used instead of its OID.
+                        */
+                       attr.da = dict_attrbyvalue(((DICT_ATTR *)&attr.unknown.da)->attr,
+                                                  ((DICT_ATTR *)&attr.unknown.da)->vendor);
+                       if (attr.da) {
+                               vpt->auto_converted = true;
+                               goto do_num;
                        }
 
-               } else {
-                       DICT_ATTR const *da;
+                       if (!allow_unknown) {
+                               fr_strerror_printf("Unknown attribute");
+                               return -(a - name);
+                       }
 
                        /*
-                        *      Attribute may be present with multiple names
+                        *      Unknown attributes can't be encoded, as we don't
+                        *      know how to encode them!
                         */
-                       da = dict_attrbyname(vpt->tmpl_da->name);
-                       if (!da) {
-                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                            "attribute \"%s\" (%s) not found in global dictionary",
-                                            file, line, vpt->tmpl_da->name,
-                                            fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"));
-                               fr_assert(0);
-                               fr_exit_now(1);
-                       }
+                       attr.da = (DICT_ATTR *)&attr.unknown.da;
 
-                       if ((da->type == PW_TYPE_COMBO_IP_ADDR) && (da->type != vpt->tmpl_da->type)) {
-                               da = dict_attrbytype(vpt->tmpl_da->attr, vpt->tmpl_da->vendor, vpt->tmpl_da->type);
-                               if (!da) {
-                                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                                    "attribute \"%s\" variant (%s) not found in global dictionary",
-                                                    file, line, vpt->tmpl_da->name,
-                                                    fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"));
-                                       fr_assert(0);
-                                       fr_exit_now(1);
-                               }
-                       }
+                       goto do_num; /* unknown attributes can't have tags */
+               }
 
-                       if (da != vpt->tmpl_da) {
-                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
-                                            "dictionary pointer %p \"%s\" (%s) "
-                                            "and global dictionary pointer %p \"%s\" (%s) differ",
-                                            file, line,
-                                            vpt->tmpl_da, vpt->tmpl_da->name,
-                                            fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"),
-                                            da, da->name,
-                                            fr_int2str(dict_attr_types, da->type, "<INVALID>"));
-                               fr_assert(0);
-                               fr_exit_now(1);
+               /*
+                *      Can't parse it as an attribute, might be a literal string
+                *      let the caller decide.
+                *
+                *      Don't alter the fr_strerror buffer, should contain the parse
+                *      error from dict_unknown_from_substr.
+                */
+               if (!allow_undefined) return -(a - name);
+
+               /*
+                *      Copy the name to a field for later resolution
+                */
+               type = TMPL_TYPE_ATTR_UNDEFINED;
+               for (q = attr.unknown.name; dict_attr_allowed_chars[(int) *p]; *q++ = *p++) {
+                       if (q >= (attr.unknown.name + sizeof(attr.unknown.name) - 1)) {
+                               fr_strerror_printf("Attribute name is too long");
+                               return -(p - name);
                        }
                }
-               break;
+               *q = '\0';
 
-       case TMPL_TYPE_LIST:
-               if (CHECK_ZEROED(vpt->data.attribute)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST"
-                                    "has non-zero bytes after the data.attribute struct in the union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+               goto do_num;
+       }
 
-               if (vpt->tmpl_da != NULL) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST da pointer was NULL", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
+       /*
+        *      The string MIGHT have a tag.
+        */
+       if (*p == ':') {
+               if (attr.da && !attr.da->flags.has_tag) { /* Lists don't have a da */
+                       fr_strerror_printf("Attribute '%s' cannot have a tag", attr.da->name);
+                       return -(p - name);
                }
-               break;
 
-       case TMPL_TYPE_DATA:
-               if (CHECK_ZEROED(vpt->data.literal)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
-                                    "has non-zero bytes after the data.literal struct in the union",
-                                    file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
+               num = strtol(p + 1, &q, 10);
+               if ((num > 0x1f) || (num < 0)) {
+                       fr_strerror_printf("Invalid tag value '%li' (should be between 0-31)", num);
+                       return -((p + 1)- name);
                }
 
-               if (vpt->tmpl_data_type == PW_TYPE_INVALID) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
-                                    "PW_TYPE_INVALID (uninitialised)", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+               attr.tag = num;
+               p = q;
+       }
 
-               if (vpt->tmpl_data_type >= PW_TYPE_MAX) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
-                                    "%i (outside the range of PW_TYPEs)", file, line, vpt->tmpl_data_type);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-               /*
-                *      Unlike VALUE_PAIRs we can't guarantee that VALUE_PAIR_TMPL buffers will
-                *      be talloced. They may be allocated on the stack or in global variables.
-                */
-               switch (vpt->tmpl_data_type) {
-               case PW_TYPE_STRING:
-               if (vpt->tmpl_data.vp_strvalue[vpt->tmpl_data_length] != '\0') {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
-                                    "terminated", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+do_num:
+       if (*p == '\0') goto finish;
+
+       if (*p == '[') {
+               p++;
+
+               switch (*p) {
+               case '#':
+                       attr.num = NUM_COUNT;
+                       p++;
                        break;
 
-               case PW_TYPE_TLV:
-               case PW_TYPE_OCTETS:
+               case '*':
+                       attr.num = NUM_ALL;
+                       p++;
+                       break;
+
+               case 'n':
+                       attr.num = NUM_LAST;
+                       p++;
                        break;
 
                default:
-                       if (vpt->tmpl_data_length == 0) {
-                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA data pointer not NULL "
-                                            "but len field is zero", file, line);
-                               fr_assert(0);
-                               fr_exit_now(1);
+                       num = strtol(p, &q, 10);
+                       if (p == q) {
+                               fr_strerror_printf("Array index is not an integer");
+                               return -(p - name);
                        }
-               }
-
-               break;
 
-       case TMPL_TYPE_REGEX:
-               /*
-                *      iflag field is used for non compiled regexes too.
-                */
-               if (CHECK_ZEROED(vpt->data.preg)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
-                                    "has non-zero bytes after the data.preg struct in the union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
+                       if ((num > 1000) || (num < 0)) {
+                               fr_strerror_printf("Invalid array reference '%li' (should be between 0-1000)", num);
+                               return -(p - name);
+                       }
+                       attr.num = num;
+                       p = q;
+                       break;
                }
 
-               if (vpt->tmpl_preg != NULL) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
-                                    "preg field was not nULL", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
+               if (*p != ']') {
+                       fr_strerror_printf("No closing ']' for array index");
+                       return -(p - name);
                }
+               p++;
+       }
 
-               if ((vpt->tmpl_iflag != true) && (vpt->tmpl_iflag != false)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
-                                    "iflag field was neither true or false", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+finish:
+       vpt->type = type;
+       vpt->name = name;
+       vpt->len = p - name;
 
-               if ((vpt->tmpl_mflag != true) && (vpt->tmpl_mflag != false)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
-                                    "mflag field was neither true or false", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+       /*
+        *      Copy over the attribute definition, now we're
+        *      sure what we were passed is valid.
+        */
+       memcpy(&vpt->data.attribute, &attr, sizeof(vpt->data.attribute));
+       if ((vpt->type == TMPL_TYPE_ATTR) && attr.da->flags.is_unknown) {
+               vpt->tmpl_da = (DICT_ATTR *)&vpt->data.attribute.unknown.da;
+       }
 
-               break;
+       VERIFY_TMPL(vpt);
 
-       case TMPL_TYPE_REGEX_STRUCT:
-               if (CHECK_ZEROED(vpt->data.preg)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
-                                    "has non-zero bytes after the data.preg struct in the union", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+       return vpt->len;
+}
 
-               if (vpt->tmpl_preg == NULL) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
-                                    "comp field was NULL", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
+ *
+ * @note Unlike #tmpl_from_attr_substr this function will error out if the entire
+ *     name string isn't parsed.
+ *
+ * @copydetails tmpl_from_attr_substr
+ */
+ssize_t tmpl_from_attr_str(vp_tmpl_t *vpt, char const *name,
+                          request_refs_t request_def, pair_lists_t list_def,
+                          bool allow_unknown, bool allow_undefined)
+{
+       ssize_t slen;
 
-               if ((vpt->tmpl_iflag != true) && (vpt->tmpl_iflag != false)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
-                                    "iflag field was neither true or false", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
+       slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
+       if (slen <= 0) return slen;
+       if (name[slen] != '\0') {
+               /* This looks wrong, but it produces meaningful errors for unknown attrs with tags */
+               fr_strerror_printf("Unexpected text after %s", fr_int2str(tmpl_names, vpt->type, "<INVALID>"));
+               return -slen;
+       }
 
-               if ((vpt->tmpl_mflag != true) && (vpt->tmpl_mflag != false)) {
-                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
-                                    "mflag field was neither true or false", file, line);
-                       fr_assert(0);
-                       fr_exit_now(1);
-               }
-               break;
+       VERIFY_TMPL(vpt);
 
-       case TMPL_TYPE_UNKNOWN:
-               rad_assert(0);
-       }
+       return slen;
 }
-#endif
 
-/** Initialise stack allocated value_pair_tmpl_t
+/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
  *
+ * @param[in,out] ctx to allocate #vp_tmpl_t in.
+ * @param[out] out Where to write pointer to new #vp_tmpl_t.
+ * @param[in] name of attribute including #request_refs and #pair_lists qualifiers.
+ *     If only #request_refs #pair_lists qualifiers are found, a #TMPL_TYPE_LIST
+ *     #vp_tmpl_t will be produced.
+ * @param[in] request_def The default #REQUEST to set if no #request_refs qualifiers are
+ *     found in name.
+ * @param[in] list_def The default list to set if no #pair_lists qualifiers are found in
+ *     name.
+ * @param[in] allow_unknown If true attributes in the format accepted by
+ *     #dict_unknown_from_substr will be allowed, even if they're not in the main
+ *     dictionaries.
+ *     If an unknown attribute is found a #TMPL_TYPE_ATTR #vp_tmpl_t will be
+ *     produced with the unknown #DICT_ATTR stored in the ``unknown.da`` buffer.
+ *     This #DICT_ATTR will have its ``flags.is_unknown`` field set to true.
+ *     If #tmpl_from_attr_substr is being called on startup, the #vp_tmpl_t may be
+ *     passed to #tmpl_define_unknown_attr to add the unknown attribute to the main
+ *     dictionary.
+ *     If the unknown attribute is not added to the main dictionary the #vp_tmpl_t
+ *     cannot be used to search for a #VALUE_PAIR in a #REQUEST.
+ * @param[in] allow_undefined If true, we don't generate a parse error on unknown attributes.
+ *     If an unknown attribute is found a #TMPL_TYPE_ATTR_UNDEFINED #vp_tmpl_t
+ *     will be produced.
+ * @return <= 0 on error (offset as negative integer), > 0 on success
+ *     (number of bytes parsed).
+ *
+ * @see REMARKER to produce pretty error markers from the return value.
  */
-value_pair_tmpl_t *tmpl_init(value_pair_tmpl_t *vpt, tmpl_type_t type, char const *name, ssize_t len)
+ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name,
+                              request_refs_t request_def, pair_lists_t list_def,
+                              bool allow_unknown, bool allow_undefined)
 {
-       rad_assert(vpt);
-       rad_assert(type != TMPL_TYPE_UNKNOWN);
-       rad_assert(type <= TMPL_TYPE_NULL);
+       ssize_t slen;
+       vp_tmpl_t *vpt;
 
-       memset(vpt, 0, sizeof(value_pair_tmpl_t));
-       vpt->type = type;
+       MEM(vpt = talloc(ctx, vp_tmpl_t)); /* tmpl_from_attr_substr zeros it */
 
-       if (name) {
-               vpt->name = name;
-               vpt->len = len < 0 ? strlen(name) :
-                                    (size_t) len;
+       slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
+       if (slen <= 0) {
+               TALLOC_FREE(vpt);
+               return slen;
        }
-       return vpt;
+       vpt->name = talloc_strndup(vpt, vpt->name, slen);
+
+       VERIFY_TMPL(vpt);
+
+       *out = vpt;
+
+       return slen;
 }
 
-/** Allocate and initialise heap allocated value_pair_tmpl_t
+/** Parse a string into a TMPL_TYPE_ATTR_* or #TMPL_TYPE_LIST type #vp_tmpl_t
  *
+ * @note Unlike #tmpl_afrom_attr_substr this function will error out if the entire
+ *     name string isn't parsed.
+ *
+ * @copydetails tmpl_afrom_attr_substr
  */
-value_pair_tmpl_t *tmpl_alloc(TALLOC_CTX *ctx, tmpl_type_t type, char const *name, ssize_t len)
+ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name,
+                           request_refs_t request_def, pair_lists_t list_def,
+                           bool allow_unknown, bool allow_undefined)
 {
-       char *p;
-       value_pair_tmpl_t *vpt;
+       ssize_t slen;
+       vp_tmpl_t *vpt;
 
-       rad_assert(type != TMPL_TYPE_UNKNOWN);
-       rad_assert(type <= TMPL_TYPE_NULL);
+       MEM(vpt = talloc(ctx, vp_tmpl_t)); /* tmpl_from_attr_substr zeros it */
 
-       vpt = talloc_zero(ctx, value_pair_tmpl_t);
-       if (!vpt) return NULL;
-       vpt->type = type;
-       if (name) {
-               vpt->name = p = len < 0 ? talloc_strdup(vpt, name) :
-                                         talloc_memdup(vpt, name, len + 1);
-               talloc_set_type(vpt->name, char);
-               vpt->len = talloc_array_length(vpt->name) - 1;
-               p[vpt->len] = '\0';
+       slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
+       if (slen <= 0) {
+               TALLOC_FREE(vpt);
+               return slen;
        }
+       if (name[slen] != '\0') {
+               /* This looks wrong, but it produces meaningful errors for unknown attrs with tags */
+               fr_strerror_printf("Unexpected text after %s", fr_int2str(tmpl_names, vpt->type, "<INVALID>"));
+               TALLOC_FREE(vpt);
+               return -slen;
+       }
+       vpt->name = talloc_strndup(vpt, vpt->name, vpt->len);
 
-       return vpt;
+       VERIFY_TMPL(vpt);
+
+       *out = vpt;
+
+       return slen;
 }
 
-/** Parse qualifiers to convert attrname into a value_pair_tmpl_t.
+/** Convert an arbitrary string into a #vp_tmpl_t
  *
- * VPTs are used in various places where we need to pre-parse configuration
- * sections into attribute mappings.
+ * @note Unlike #tmpl_afrom_attr_str return code 0 doesn't necessarily indicate failure,
+ *     may just mean a 0 length string was parsed.
  *
- * @note The name field is just a copy of the input pointer, if you know that
- * string might be freed before you're done with the vpt use tmpl_afrom_attr_str
- * instead.
+ * @note xlats and regexes are left uncompiled.  This is to support the two pass parsing
+ *     done by the modcall code.  Compilation on pass1 of that code could fail, as
+ *     attributes or xlat functions registered by modules may not be available (yet).
  *
- * @param[out] vpt to modify.
- * @param[in] name of attribute 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.
- * @param[in] allow_unknown If true attributes in the format accepted by dict_unknown_from_substr
- *     will be allowed, even if they're not in the main dictionaries.
- * @param[in] allow_undefined If true, we don't generate a parse error on unknown
- *     attributes, and instead set type to TMPL_TYPE_ATTR_UNDEFINED.
- * @return <= 0 on error (offset as negative integer), > 0 on success (number of bytes parsed)
+ * @note For details of attribute parsing see #tmpl_from_attr_substr.
+ *
+ * @param[in,out] ctx To allocate #vp_tmpl_t in.
+ * @param[out] out Where to write the pointer to the new #vp_tmpl_t.
+ * @param[in] in String to convert to a #vp_tmpl_t.
+ * @param[in] inlen length of string to convert.
+ * @param[in] type of quoting around value. May be one of:
+ *     - #T_BARE_WORD - If string begins with ``&`` produces #TMPL_TYPE_ATTR,
+ *       #TMPL_TYPE_ATTR_UNDEFINED, #TMPL_TYPE_LIST or error.
+ *       If string does not begin with ``&`` produces #TMPL_TYPE_LITERAL,
+ *       #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
+ *     - #T_SINGLE_QUOTED_STRING - Produces #TMPL_TYPE_LITERAL
+ *     - #T_DOUBLE_QUOTED_STRING - Produces #TMPL_TYPE_XLAT or #TMPL_TYPE_LITERAL (if
+ *       string doesn't contain ``%``).
+ *     - #T_BACK_QUOTED_STRING - Produces #TMPL_TYPE_EXEC
+ *     - #T_OP_REG_EQ - Produces #TMPL_TYPE_REGEX
+ * @param[in] request_def The default #REQUEST to set if no #request_refs qualifiers are
+ *     found in name.
+ * @param[in] list_def The default list to set if no #pair_lists qualifiers are found in
+ *     name.
+ * @param[in] do_unescape whether or not we should do unescaping. Should be false if the
+ *     caller already did it.
+ * @return <= 0 on error (offset as negative integer), > 0 on success
+ *     (number of bytes parsed).
+ *     @see REMARKER to produce pretty error markers from the return value.
+ *
+ * @see tmpl_from_attr_substr
  */
-ssize_t tmpl_from_attr_substr(value_pair_tmpl_t *vpt, char const *name,
-                             request_refs_t request_def, pair_lists_t list_def,
-                             bool allow_unknown, bool allow_undefined)
+ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *in, size_t inlen, FR_TOKEN type,
+                      request_refs_t request_def, pair_lists_t list_def, bool do_unescape)
 {
+       bool do_xlat;
+       char quote;
        char const *p;
-       long num;
-       char *q;
-       tmpl_type_t type = TMPL_TYPE_ATTR;
+       ssize_t slen;
+       PW_TYPE data_type = PW_TYPE_STRING;
+       vp_tmpl_t *vpt = NULL;
+       value_data_t data;
 
-       value_pair_tmpl_attr_t attr;    /* So we don't fill the tmpl with junk and then error out */
+       switch (type) {
+       case T_BARE_WORD:
+               /*
+                *      If we can parse it as an attribute, it's an attribute.
+                *      Otherwise, treat it as a literal.
+                */
+               quote = '\0';
 
-       memset(vpt, 0, sizeof(*vpt));
-       memset(&attr, 0, sizeof(attr));
+               slen = tmpl_afrom_attr_str(ctx, &vpt, in, request_def, list_def, true, (in[0] == '&'));
+               if ((in[0] == '&') && (slen <= 0)) return slen;
+               if (slen > 0) break;
+               goto parse;
 
-       p = name;
+       case T_SINGLE_QUOTED_STRING:
+               quote = '\'';
 
-       if (*p == '&') p++;
+       parse:
+               if (cf_new_escape && do_unescape) {
+                       slen = value_data_from_str(ctx, &data, &data_type, NULL, in, inlen, quote);
+                       if (slen < 0) return 0;
 
-       p += radius_request_name(&attr.request, p, request_def);
-       if (attr.request == REQUEST_UNKNOWN) {
-               fr_strerror_printf("Invalid request qualifier");
-               return -(p - name);
-       }
-
-       /*
-        *      Finding a list qualifier is optional
-        */
-       p += radius_list_name(&attr.list, p, list_def);
-       if (attr.list == PAIR_LIST_UNKNOWN) {
-               fr_strerror_printf("Invalid list qualifier");
-               return -(p - name);
-       }
-
-       if (*p == '\0') {
-               type = TMPL_TYPE_LIST;
-               goto finish;
-       }
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       talloc_free(data.ptr);
+               } else {
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, in, inlen);
+               }
+               vpt->quote = quote;
+               slen = vpt->len;
+               break;
 
-       attr.tag = TAG_ANY;
-       attr.num = NUM_ANY;
+       case T_DOUBLE_QUOTED_STRING:
+               do_xlat = false;
 
-       attr.da = dict_attrbyname_substr(&p);
-       if (!attr.da) {
-               char const *a;
+               p = in;
+               while (*p) {
+                       if (do_unescape) { /* otherwise \ is just another character */
+                               if (*p == '\\') {
+                                       if (!p[1]) break;
+                                       p += 2;
+                                       continue;
+                               }
+                       }
 
-               /*
-                *      Record start of attribute in case we need to error out.
-                */
-               a = p;
+                       if (*p == '%') {
+                               do_xlat = true;
+                               break;
+                       }
 
-               fr_strerror();  /* Clear out any existing errors */
+                       p++;
+               }
 
                /*
-                *      Attr-1.2.3.4 is OK.
+                *      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 (dict_unknown_from_substr((DICT_ATTR *)&attr.unknown.da, &p) == 0) {
-                       /*
-                        *      Check what we just parsed really hasn't been defined
-                        *      in the main dictionaries.
-                        */
-                       attr.da = dict_attrbyvalue(((DICT_ATTR *)&attr.unknown.da)->attr,
-                                                  ((DICT_ATTR *)&attr.unknown.da)->vendor);
-                       if (attr.da) goto do_tag;
+               if (cf_new_escape && do_unescape) {
+                       slen = value_data_from_str(ctx, &data, &data_type, NULL, in, inlen, '"');
+                       if (slen < 0) return slen;
 
-                       if (!allow_unknown) {
-                               fr_strerror_printf("Unknown attribute");
-                               return -(a - name);
+                       if (do_xlat) {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, data.strvalue,
+                                                talloc_array_length(data.strvalue) - 1);
+                       } else {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue,
+                                                talloc_array_length(data.strvalue) - 1);
+                               vpt->quote = '"';
+                       }
+                       talloc_free(data.ptr);
+               } else {
+                       if (do_xlat) {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, in, inlen);
+                       } else {
+                               vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, in, inlen);
+                               vpt->quote = '"';
                        }
-
-                       attr.da = (DICT_ATTR *)&attr.unknown.da;
-                       goto skip_tag; /* unknown attributes can't have tags */
                }
+               slen = vpt->len;
+               break;
 
-               /*
-                *      Can't parse it as an attribute, might be a literal string
-                *      let the caller decide.
-                *
-                *      Don't alter the fr_strerror buffer, should contain the parse
-                *      error from dict_unknown_from_substr.
-                */
-               if (!allow_undefined) return -(a - name);
+       case T_BACK_QUOTED_STRING:
+               if (cf_new_escape && do_unescape) {
+                       slen = value_data_from_str(ctx, &data, &data_type, NULL, in, inlen, '`');
+                       if (slen < 0) return slen;
 
-               /*
-                *      Copy the name to a field for later resolution
-                */
-               type = TMPL_TYPE_ATTR_UNDEFINED;
-               for (q = attr.unknown.name; dict_attr_allowed_chars[(int) *p]; *q++ = *p++) {
-                       if (q >= (attr.unknown.name + sizeof(attr.unknown.name) - 1)) {
-                               fr_strerror_printf("Attribute name is too long");
-                               return -(p - name);
-                       }
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, data.strvalue, talloc_array_length(data.strvalue) - 1);
+                       talloc_free(data.ptr);
+               } else {
+                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, in, inlen);
                }
-               *q = '\0';
+               slen = vpt->len;
+               break;
+
+       case T_OP_REG_EQ: /* hack */
+               vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX, in, inlen);
+               slen = vpt->len;
+               break;
 
-               goto skip_tag;
+       default:
+               rad_assert(0);
+               return 0;       /* 0 is an error here too */
        }
 
-do_tag:
-       type = TMPL_TYPE_ATTR;
+       rad_assert((slen >= 0) && (vpt != NULL));
 
-       /*
-        *      The string MIGHT have a tag.
-        */
-       if (*p == ':') {
-               if (!attr.da->flags.has_tag) {
-                       fr_strerror_printf("Attribute '%s' cannot have a tag", attr.da->name);
-                       return -(p - name);
-               }
+       VERIFY_TMPL(vpt);
 
-               num = strtol(p + 1, &q, 10);
-               if ((num > 0x1f) || (num < 0)) {
-                       fr_strerror_printf("Invalid tag value '%li' (should be between 0-31)", num);
-                       return -((p + 1)- name);
-               }
+       *out = vpt;
 
-               attr.tag = num;
-               p = q;
-       }
+       return slen;
+}
+/* @} **/
 
-skip_tag:
-       if (*p == '\0') goto finish;
+/** @name Cast or convert #vp_tmpl_t
+ *
+ * #tmpl_cast_in_place can be used to convert #TMPL_TYPE_LITERAL to a #TMPL_TYPE_DATA of a
+ *  specified #PW_TYPE.
+ *
+ * #tmpl_cast_in_place_str does the same as #tmpl_cast_in_place, but will always convert to
+ * #PW_TYPE #PW_TYPE_STRING.
+ *
+ * #tmpl_cast_to_vp does the same as #tmpl_cast_in_place, but outputs a #VALUE_PAIR.
+ *
+ * #tmpl_define_unknown_attr converts a #TMPL_TYPE_ATTR with an unknown #DICT_ATTR to a
+ * #TMPL_TYPE_ATTR with a known #DICT_ATTR, by adding the unknown #DICT_ATTR to the main
+ * dictionary, and updating the ``tmpl_da`` pointer.
+ * @{
+ */
 
-       if (*p == '[') {
-               p++;
+/** Convert #vp_tmpl_t of type #TMPL_TYPE_LITERAL or #TMPL_TYPE_DATA to #TMPL_TYPE_DATA of type specified
+ *
+ * @note Conversion is done in place.
+ * @note Irrespective of whether the #vp_tmpl_t was #TMPL_TYPE_LITERAL or #TMPL_TYPE_DATA,
+ *     on successful cast it will be #TMPL_TYPE_DATA.
+ *
+ * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_LITERAL
+ *     or #TMPL_TYPE_DATA.
+ * @param[in] type to cast to.
+ * @param[in] enumv Enumerated dictionary values associated with a #DICT_ATTR.
+ * @return 0 on success, -1 on failure.
+ */
+int tmpl_cast_in_place(vp_tmpl_t *vpt, PW_TYPE type, DICT_ATTR const *enumv)
+{
+       ssize_t ret;
 
-               switch (*p) {
-               case '#':
-                       attr.num = NUM_COUNT;
-                       p++;
-                       break;
+       VERIFY_TMPL(vpt);
 
-               case '*':
-                       attr.num = NUM_ALL;
-                       p++;
-                       break;
+       rad_assert(vpt != NULL);
+       rad_assert((vpt->type == TMPL_TYPE_LITERAL) || (vpt->type == TMPL_TYPE_DATA));
 
-               case 'n':
-                       attr.num = NUM_LAST;
-                       p++;
+       switch (vpt->type) {
+       case TMPL_TYPE_LITERAL:
+               vpt->tmpl_data_type = type;
+
+               /*
+                *      Why do we pass a pointer to the tmpl type? Goddamn WiMAX.
+                */
+               ret = value_data_from_str(vpt, &vpt->tmpl_data_value, &vpt->tmpl_data_type,
+                                         enumv, vpt->name, vpt->len, '\0');
+               if (ret < 0) return -1;
+
+               vpt->type = TMPL_TYPE_DATA;
+               vpt->tmpl_data_length = (size_t) ret;
+               break;
+
+       case TMPL_TYPE_DATA:
+       {
+               value_data_t new;
+
+               if (type == vpt->tmpl_data_type) return 0;      /* noop */
+
+               ret = value_data_cast(vpt, &new, type, enumv, vpt->tmpl_data_type,
+                                     NULL, &vpt->tmpl_data_value, vpt->tmpl_data_length);
+               if (ret < 0) return -1;
+
+               /*
+                *      Free old value buffers
+                */
+               switch (vpt->tmpl_data_type) {
+               case PW_TYPE_STRING:
+               case PW_TYPE_OCTETS:
+                       talloc_free(vpt->tmpl_data_value.ptr);
                        break;
 
                default:
-                       num = strtol(p, &q, 10);
-                       if (p == q) {
-                               fr_strerror_printf("Array index is not an integer");
-                               return -(p - name);
-                       }
-
-                       if ((num > 1000) || (num < 0)) {
-                               fr_strerror_printf("Invalid array reference '%li' (should be between 0-1000)", num);
-                               return -(p - name);
-                       }
-                       attr.num = num;
-                       p = q;
                        break;
                }
 
-               if (*p != ']') {
-                       fr_strerror_printf("No closing ']' for array index");
-                       return -(p - name);
-               }
-               p++;
+               memcpy(&vpt->tmpl_data_value, &new, sizeof(vpt->tmpl_data_value));
+               vpt->tmpl_data_type = type;
+               vpt->tmpl_data_length = (size_t) ret;
        }
+               break;
 
-finish:
-       vpt->type = type;
-       vpt->name = name;
-       vpt->len = p - name;
-
-       /*
-        *      Copy over the attribute definition, now we're
-        *      sure what we were passed is valid.
-        */
-       memcpy(&vpt->data.attribute, &attr, sizeof(vpt->data.attribute));
-       if ((vpt->type == TMPL_TYPE_ATTR) && attr.da->flags.is_unknown) {
-               vpt->tmpl_da = (DICT_ATTR *)&vpt->data.attribute.unknown.da;
+       default:
+               rad_assert(0);
        }
 
        VERIFY_TMPL(vpt);
 
-       return vpt->len;
+       return 0;
 }
 
-/** Parse qualifiers to convert an attrname into a value_pair_tmpl_t.
+/** Convert #vp_tmpl_t of type #TMPL_TYPE_LITERAL to #TMPL_TYPE_DATA of type #PW_TYPE_STRING
  *
- * VPTs are used in various places where we need to pre-parse configuration
- * sections into attribute mappings.
+ * @note Conversion is done in place.
  *
- * @note The name field is just a copy of the input pointer, if you know that
- * string might be freed before you're done with the vpt use tmpl_afrom_attr_str
- * instead.
- *
- * @param[out] vpt to modify.
- * @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.
- * @param[in] allow_unknown If true attributes in the format accepted by dict_unknown_from_substr
- *     will be allowed, even if they're not in the main dictionaries.
- * @param[in] allow_undefined If true, we don't generate a parse error on unknown
- *     attributes, and instead set type to TMPL_TYPE_ATTR_UNDEFINED.
- * @return <= 0 on error (offset as negative integer), > 0 on success (number of bytes parsed)
+ * @param[in,out] vpt The template to modify. Must be of type #TMPL_TYPE_LITERAL.
  */
-ssize_t tmpl_from_attr_str(value_pair_tmpl_t *vpt, char const *name,
-                          request_refs_t request_def, pair_lists_t list_def,
-                          bool allow_unknown, bool allow_undefined)
+void tmpl_cast_in_place_str(vp_tmpl_t *vpt)
 {
-       ssize_t slen;
-
-       slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
-       if (slen <= 0) return slen;
-       if (name[slen] != '\0') {
-               fr_strerror_printf("Unexpected text after attribute name");
-               return -slen;
-       }
+       rad_assert(vpt != NULL);
+       rad_assert(vpt->type == TMPL_TYPE_LITERAL);
 
-       VERIFY_TMPL(vpt);
+       vpt->tmpl_data.vp_strvalue = talloc_typed_strdup(vpt, vpt->name);
+       rad_assert(vpt->tmpl_data.vp_strvalue != NULL);
 
-       return slen;
+       vpt->type = TMPL_TYPE_DATA;
+       vpt->tmpl_data_type = PW_TYPE_STRING;
+       vpt->tmpl_data_length = talloc_array_length(vpt->tmpl_data.vp_strvalue) - 1;
 }
 
-/** Parse qualifiers to convert attrname into a value_pair_tmpl_t.
- *
- * VPTs are used in various places where we need to pre-parse configuration
- * sections into attribute mappings.
- *
- * @param[in] ctx for talloc
- * @param[out] out Where to write the pointer to the new value_pair_tmpl_t.
- * @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.
- * @param[in] allow_unknown If true attributes in the format accepted by dict_unknown_from_substr
- *     will be allowed, even if they're not in the main dictionaries.
- * @param[in] allow_undefined If true, we don't generate a parse error on unknown
- *     attributes, and instead set type to TMPL_TYPE_ATTR_UNDEFINED.
- * @return <= 0 on error (offset as negative integer), > 0 on success (number of bytes parsed)
+/** Expand a #vp_tmpl_t to a string, parse it as an attribute of type cast, create a #VALUE_PAIR from the result
+ *
+ * @note Like #tmpl_expand, but produces a #VALUE_PAIR.
+ *
+ * @param out Where to write pointer to the new #VALUE_PAIR.
+ * @param request The current #REQUEST.
+ * @param vpt to cast. Must be one of the following types:
+ *     - #TMPL_TYPE_LITERAL
+ *     - #TMPL_TYPE_EXEC
+ *     - #TMPL_TYPE_XLAT
+ *     - #TMPL_TYPE_XLAT_STRUCT
+ *     - #TMPL_TYPE_ATTR
+ *     - #TMPL_TYPE_DATA
+ * @param cast type of #VALUE_PAIR to create.
+ * @return 0 on success, -1 on failure.
  */
-ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name,
-                           request_refs_t request_def, pair_lists_t list_def,
-                           bool allow_unknown, bool allow_undefined)
+int tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request,
+                   vp_tmpl_t const *vpt, DICT_ATTR const *cast)
 {
-       ssize_t slen;
-       value_pair_tmpl_t *vpt;
-
-       MEM(vpt = talloc(ctx, value_pair_tmpl_t)); /* tmpl_from_attr_substr zeros it */
-
-       slen = tmpl_from_attr_substr(vpt, name, request_def, list_def, allow_unknown, allow_undefined);
-       if (slen <= 0) {
-               tmpl_free(&vpt);
-               return slen;
-       }
-       if (name[slen] != '\0') {
-               fr_strerror_printf("Unexpected text after attribute name");
-               tmpl_free(&vpt);
-               return -slen;
-       }
-       vpt->name = talloc_strndup(vpt, vpt->name, vpt->len);
+       int rcode;
+       VALUE_PAIR *vp;
+       value_data_t data;
+       char *p;
 
        VERIFY_TMPL(vpt);
 
-       *out = vpt;
-
-       return slen;
-}
+       *out = NULL;
 
-/** Release memory allocated to value pair template.
- *
- * @param[in,out] tmpl to free.
- */
-void tmpl_free(value_pair_tmpl_t **tmpl)
-{
-       if (*tmpl == NULL) return;
+       vp = fr_pair_afrom_da(request, cast);
+       if (!vp) return -1;
 
-       if ((*tmpl)->type != TMPL_TYPE_UNKNOWN) VERIFY_TMPL(*tmpl);
+       if (vpt->type == TMPL_TYPE_DATA) {
+               VERIFY_VP(vp);
+               rad_assert(vp->da->type == vpt->tmpl_data_type);
 
-       dict_attr_free(&((*tmpl)->tmpl_da));
+               value_data_copy(vp, &vp->data, vpt->tmpl_data_type, &vpt->tmpl_data_value, vpt->tmpl_data_length);
+               *out = vp;
+               return 0;
+       }
 
-       talloc_free(*tmpl);
+       rcode = tmpl_aexpand(vp, &p, request, vpt, NULL, NULL);
+       if (rcode < 0) {
+               fr_pair_list_free(&vp);
+               return rcode;
+       }
+       data.strvalue = p;
 
-       *tmpl = NULL;
-}
+       /*
+        *      New escapes: strings are in binary form.
+        */
+       if (cf_new_escape && (vp->da->type == PW_TYPE_STRING)) {
+               vp->data.ptr = talloc_steal(vp, data.ptr);
+               vp->vp_length = rcode;
+
+       } else if (fr_pair_value_from_str(vp, data.strvalue, rcode) < 0) {
+               talloc_free(data.ptr);
+               fr_pair_list_free(&vp);
+               return -1;
+       }
+
+       *out = vp;
+       return 0;
+}
 
-/**  Print a template to a string
+/** Add an unknown #DICT_ATTR specified by a #vp_tmpl_t to the main dictionary
  *
- * @param[out] buffer for the output string
- * @param[in] bufsize of the buffer
- * @param[in] vpt to print
- * @param[in] values Used for integer attributes only. DICT_ATTR to use when mapping integer values to strings.
- * @return the size of the string written to the output buffer.
+ * @param vpt to add. ``tmpl_da`` pointer will be updated to point to the
+ *     #DICT_ATTR inserted into the dictionary.
+ * @return 0 on success, -1 on failure.
  */
-size_t tmpl_prints(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt, DICT_ATTR const *values)
+int tmpl_define_unknown_attr(vp_tmpl_t *vpt)
 {
-       size_t len;
-       char c;
-       char const *p;
-       char *q = buffer;
+       DICT_ATTR const *da;
 
-       if (!vpt) {
-               *buffer = '\0';
-               return 0;
-       }
+       if (!vpt) return -1;
 
        VERIFY_TMPL(vpt);
 
-       switch (vpt->type) {
-       default:
-               return 0;
+       if (vpt->type != TMPL_TYPE_ATTR) return 0;
 
-       case TMPL_TYPE_REGEX:
-       case TMPL_TYPE_REGEX_STRUCT:
-               c = '/';
-               break;
+       if (!vpt->tmpl_da->flags.is_unknown) return 0;
 
-       case TMPL_TYPE_XLAT:
-       case TMPL_TYPE_XLAT_STRUCT:
-               c = '"';
-               break;
+       da = dict_unknown_add(vpt->tmpl_da);
+       if (!da) return -1;
+       vpt->tmpl_da = da;
+       return 0;
+}
+/* @} **/
 
-       case TMPL_TYPE_LIST:
-       case TMPL_TYPE_LITERAL: /* single-quoted or bare word */
-               /*
-                *      Hack
-                */
-               for (p = vpt->name; *p != '\0'; p++) {
-                       if (*p == ' ') break;
-                       if (*p == '\'') break;
-                       if (!dict_attr_allowed_chars[(int) *p]) break;
-               }
+/** @name Resolve a #vp_tmpl_t outputting the result in various formats
+ *
+ * @{
+ */
 
-               if (!*p) {
-                       strlcpy(buffer, vpt->name, bufsize);
-                       return strlen(buffer);
-               }
+/** Expand a #vp_tmpl_t to a string writing the result to a buffer
+ *
+ * The intended use of #tmpl_expand and #tmpl_aexpand is for modules to easily convert a #vp_tmpl_t
+ * provided by the conf parser, into a usable value.
+ * The value returned should be raw and undoctored for #PW_TYPE_STRING and #PW_TYPE_OCTETS types,
+ * and the printable (string) version of the data for all others.
+ *
+ * Depending what arguments are passed, either copies the value to buff, or writes a pointer
+ * to a string buffer to out. This allows the most efficient access to the value resolved by
+ * the #vp_tmpl_t, avoiding unecessary string copies.
+ *
+ * @note This function is used where raw string values are needed, which may mean the string
+ *     returned may be binary data or contain unprintable chars. #fr_prints or #fr_aprints should
+ *     be used before using these values in debug statements. #is_printable can be used to check
+ *     if the string only contains printable chars.
+ *
+ * @param out Where to write a pointer to the string buffer. On return may point to buff if
+ *     buff was used to store the value. Otherwise will point to a #value_data_t buffer,
+ *     or the name of the template. To force copying to buff, out should be NULL.
+ * @param buff Expansion buffer, may be NULL if out is not NULL, and processing #TMPL_TYPE_LITERAL
+ *     or string types.
+ * @param bufflen Length of expansion buffer.
+ * @param request Current request.
+ * @param vpt to expand. Must be one of the following types:
+ *     - #TMPL_TYPE_LITERAL
+ *     - #TMPL_TYPE_EXEC
+ *     - #TMPL_TYPE_XLAT
+ *     - #TMPL_TYPE_XLAT_STRUCT
+ *     - #TMPL_TYPE_ATTR
+ *     - #TMPL_TYPE_DATA
+ * @param escape xlat escape function (only used for xlat types).
+ * @param escape_ctx xlat escape function data.
+ * @return -1 on error, else the length of data written to buff, or pointed to by out.
+ */
+ssize_t tmpl_expand(char const **out, char *buff, size_t bufflen, REQUEST *request,
+                   vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
+{
+       VALUE_PAIR *vp;
+       ssize_t slen = -1;      /* quiet compiler */
 
-               c = '\'';
-               break;
+       VERIFY_TMPL(vpt);
 
-       case TMPL_TYPE_EXEC:
-               c = '`';
-               break;
+       rad_assert(vpt->type != TMPL_TYPE_LIST);
 
-       case TMPL_TYPE_ATTR:
-               buffer[0] = '&';
-               if (vpt->tmpl_request == REQUEST_CURRENT) {
-                       if (vpt->tmpl_list == PAIR_LIST_REQUEST) {
-                               strlcpy(buffer + 1, vpt->tmpl_da->name, bufsize - 1);
-                       } else {
-                               snprintf(buffer + 1, bufsize - 1, "%s:%s",
-                                        fr_int2str(pair_lists, vpt->tmpl_list, ""),
-                                        vpt->tmpl_da->name);
-                       }
+       if (out) *out = NULL;
+
+       switch (vpt->type) {
+       case TMPL_TYPE_LITERAL:
+               RDEBUG4("EXPAND TMPL LITERAL");
 
+               if (!out) {
+                       rad_assert(buff);
+                       memcpy(buff, vpt->name, vpt->len >= bufflen ? bufflen : vpt->len + 1);
                } else {
-                       snprintf(buffer + 1, bufsize - 1, "%s.%s:%s",
-                                fr_int2str(request_refs, vpt->tmpl_request, ""),
-                                fr_int2str(pair_lists, vpt->tmpl_list, ""),
-                                vpt->tmpl_da->name);
+                       *out = vpt->name;
                }
+               return vpt->len;
 
-               len = strlen(buffer);
-
-               if ((vpt->tmpl_tag == TAG_ANY) && (vpt->tmpl_num == NUM_ANY)) {
-                       return len;
+       case TMPL_TYPE_EXEC:
+       {
+               RDEBUG4("EXPAND TMPL EXEC");
+               rad_assert(buff);
+               if (radius_exec_program(request, buff, bufflen, NULL, request, vpt->name, NULL,
+                                       true, false, EXEC_TIMEOUT) != 0) {
+                       return -1;
                }
+               slen = strlen(buff);
+               if (out) *out = buff;
+       }
+               break;
 
-               q = buffer + len;
-               bufsize -= len;
+       case TMPL_TYPE_XLAT:
+               RDEBUG4("EXPAND TMPL XLAT");
+               rad_assert(buff);
+               /* Error in expansion, this is distinct from zero length expansion */
+               slen = radius_xlat(buff, bufflen, request, vpt->name, escape, escape_ctx);
+               if (slen < 0) return slen;
+               if (out) *out = buff;
+               break;
 
-               if (vpt->tmpl_tag != TAG_ANY) {
-                       snprintf(q, bufsize, ":%d", vpt->tmpl_tag);
-                       len = strlen(q);
-                       q += len;
-                       bufsize -= len;
+       case TMPL_TYPE_XLAT_STRUCT:
+               RDEBUG4("EXPAND TMPL XLAT STRUCT");
+               rad_assert(buff);
+               /* Error in expansion, this is distinct from zero length expansion */
+               slen = radius_xlat_struct(buff, bufflen, request, vpt->tmpl_xlat, escape, escape_ctx);
+               if (slen < 0) {
+                       return slen;
                }
+               slen = strlen(buff);
+               if (out) *out = buff;
+               break;
 
-               switch (vpt->tmpl_num) {
-               case NUM_ANY:
-                       break;
-
-               case NUM_ALL:
-                       snprintf(q, bufsize, "[*]");
-                       len = strlen(q);
-                       q += len;
-                       break;
-
-               case NUM_COUNT:
-                       snprintf(q, bufsize, "[#]");
-                       len = strlen(q);
-                       q += len;
-                       break;
+       case TMPL_TYPE_ATTR:
+       {
+               int ret;
 
-               case NUM_LAST:
-                       snprintf(q, bufsize, "[n]");
-                       len = strlen(q);
-                       q += len;
-                       break;
+               RDEBUG4("EXPAND TMPL ATTR");
+               rad_assert(buff);
+               ret = tmpl_find_vp(&vp, request, vpt);
+               if (ret < 0) return -2;
 
-               default:
-                       snprintf(q, bufsize, "[%i]", vpt->tmpl_num);
-                       len = strlen(q);
-                       q += len;
-                       break;
+               if (out && ((vp->da->type == PW_TYPE_STRING) || (vp->da->type == PW_TYPE_OCTETS))) {
+                       *out = vp->data.ptr;
+                       slen = vp->vp_length;
+               } else {
+                       if (out) *out = buff;
+                       slen = vp_prints_value(buff, bufflen, vp, '\0');
                }
+       }
+               break;
 
-               return (q - buffer);
-
-       case TMPL_TYPE_ATTR_UNDEFINED:
-               buffer[0] = '&';
-               if (vpt->tmpl_request == REQUEST_CURRENT) {
-                       if (vpt->tmpl_list == PAIR_LIST_REQUEST) {
-                               strlcpy(buffer + 1, vpt->tmpl_unknown_name, bufsize - 1);
-                       } else {
-                               snprintf(buffer + 1, bufsize - 1, "%s:%s",
-                                        fr_int2str(pair_lists, vpt->tmpl_list, ""),
-                                        vpt->tmpl_unknown_name);
-                       }
+       case TMPL_TYPE_DATA:
+       {
+               RDEBUG4("EXPAND TMPL DATA");
 
+               if (out && ((vpt->tmpl_data_type == PW_TYPE_STRING) || (vpt->tmpl_data_type == PW_TYPE_OCTETS))) {
+                       *out = vpt->tmpl_data_value.ptr;
+                       slen = vpt->tmpl_data_length;
                } else {
-                       snprintf(buffer + 1, bufsize - 1, "%s.%s:%s",
-                                fr_int2str(request_refs, vpt->tmpl_request, ""),
-                                fr_int2str(pair_lists, vpt->tmpl_list, ""),
-                                vpt->tmpl_unknown_name);
+                       if (out) *out = buff;
+                       /**
+                        *  @todo tmpl_expand should accept an enumv da from the lhs of the map.
+                        */
+                       slen = value_data_prints(buff, bufflen, vpt->tmpl_data_type, NULL, &vpt->tmpl_data_value, vpt->tmpl_data_length, '\0');
                }
+       }
+               break;
 
-               len = strlen(buffer);
+       /*
+        *      We should never be expanding these.
+        */
+       case TMPL_TYPE_UNKNOWN:
+       case TMPL_TYPE_NULL:
+       case TMPL_TYPE_LIST:
+       case TMPL_TYPE_REGEX:
+       case TMPL_TYPE_ATTR_UNDEFINED:
+       case TMPL_TYPE_REGEX_STRUCT:
+               rad_assert(0 == 1);
+               slen = -1;
+               break;
+       }
 
-               if (vpt->tmpl_num == NUM_ANY) {
-                       return len;
-               }
+       if (slen < 0) return slen;
 
-               q = buffer + len;
-               bufsize -= len;
 
-               if (vpt->tmpl_num != NUM_ANY) {
-                       snprintf(q, bufsize, "[%i]", vpt->tmpl_num);
-                       len = strlen(q);
-                       q += len;
-               }
+#if 0
+       /*
+        *      If we're doing correct escapes, we may have to re-parse the string.
+        *      If the string is from another expansion, it needs re-parsing.
+        *      Or, if it's from a "string" attribute, it needs re-parsing.
+        *      Integers, IP addresses, etc. don't need re-parsing.
+        */
+       if (cf_new_escape && (vpt->type != TMPL_TYPE_ATTR)) {
+               value_data_t    vd;
+               int             ret;
 
-               return (q - buffer);
+               PW_TYPE type = PW_TYPE_STRING;
 
-       case TMPL_TYPE_DATA:
-               return vp_data_prints_value(buffer, bufsize, vpt->tmpl_data_type, values,
-                                           &vpt->tmpl_data_value, vpt->tmpl_data_length, '\'');
+               slen = value_data_from_str(ctx, &vd, &type, NULL, *out, slen, '"');
+               talloc_free(*out);      /* free the old value */
+               *out = vd.ptr;
        }
+#endif
 
-       if (bufsize <= 3) {
-               *buffer = '\0';
-               return 0;
+       if (vpt->type == TMPL_TYPE_XLAT_STRUCT) {
+               RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
+               RDEBUG2("   --> %s", buff);
        }
 
-       *(q++) = c;
-
-       /*
-        *      Print it with appropriate escaping
-        */
-       len = fr_prints(q, bufsize - 3, vpt->name, -1, c);
-
-       q += len;
-       *(q++) = c;
-       *q = '\0';
-
-       return q - buffer;
+       return slen;
 }
 
-/** Convert module specific attribute id to value_pair_tmpl_t.
+/** Expand a template to a string, allocing a new buffer to hold the string
  *
- * @note Unlike tmpl_afrom_attr_str return code 0 doesn't indicate failure, just means it parsed a 0 length string.
+ * The intended use of #tmpl_expand and #tmpl_aexpand is for modules to easily convert a #vp_tmpl_t
+ * provided by the conf parser, into a usable value.
+ * The value returned should be raw and undoctored for #PW_TYPE_STRING and #PW_TYPE_OCTETS types,
+ * and the printable (string) version of the data for all others.
  *
- * @param[in] ctx for talloc.
- * @param[out] out Where to write the pointer to the new value_pait_tmpl_t.
- * @param[in] name string to convert.
- * @param[in] inlen length of 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 < 0 on error (offset as negative integer), >= 0 on success (number of bytes parsed)
+ * This function will always duplicate values, whereas #tmpl_expand may return a pointer to an
+ * existing buffer.
+ *
+ * @note This function is used where raw string values are needed, which may mean the string
+ *     returned may be binary data or contain unprintable chars. #fr_prints or #fr_aprints should
+ *     be used before using these values in debug statements. #is_printable can be used to check
+ *     if the string only contains printable chars.
+ *
+ * @note The type (char or uint8_t) can be obtained with talloc_get_type, and may be used as a
+ *     hint as to how to process or print the data.
+ *
+ * @param ctx to allocate new buffer in.
+ * @param out Where to write pointer to the new buffer.
+ * @param request Current request.
+ * @param vpt to expand. Must be one of the following types:
+ *     - #TMPL_TYPE_LITERAL
+ *     - #TMPL_TYPE_EXEC
+ *     - #TMPL_TYPE_XLAT
+ *     - #TMPL_TYPE_XLAT_STRUCT
+ *     - #TMPL_TYPE_ATTR
+ *     - #TMPL_TYPE_DATA
+ * @param escape xlat escape function (only used for xlat types).
+ * @param escape_ctx xlat escape function data (only used for xlat types).
+ * @return
+ *     - -1 on failure.
+ *     - The length of data written to buff, or pointed to by out.
  */
-ssize_t tmpl_afrom_str(TALLOC_CTX *ctx, value_pair_tmpl_t **out, char const *name, size_t inlen, FR_TOKEN type,
-                      request_refs_t request_def, pair_lists_t list_def)
+ssize_t tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, vp_tmpl_t const *vpt,
+                    xlat_escape_t escape, void *escape_ctx)
 {
-       char const *p;
-       ssize_t slen;
-       PW_TYPE data_type = PW_TYPE_STRING;
-       value_pair_tmpl_t *vpt = NULL;
-       value_data_t data;
-
-       switch (type) {
-       case T_BARE_WORD:
-               /*
-                *      If we can parse it as an attribute, it's an attribute.
-                *      Otherwise, treat it as a literal.
-                */
-               slen = tmpl_afrom_attr_str(ctx, &vpt, name, request_def, list_def, true, (name[0] == '&'));
-               if ((name[0] == '&') && (slen <= 0)) return slen;
-               if (slen > 0) break;
-               /* FALL-THROUGH */
+       VALUE_PAIR *vp;
+       ssize_t slen = -1;      /* quiet compiler */
 
-       case T_SINGLE_QUOTED_STRING:
-               if (cf_new_escape) {
-                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '\'');
-                       rad_assert(slen >= 0);
+       rad_assert(vpt->type != TMPL_TYPE_LIST);
 
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
-                       talloc_free(data.ptr);
-               } else {
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, name, -1);
-               }
-               slen = vpt->len;
-               break;
+       VERIFY_TMPL(vpt);
 
-       case T_DOUBLE_QUOTED_STRING:
-               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 (cf_new_escape) {
-                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '"');
-                       rad_assert(slen >= 0);
-
-                       if (*p) {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, data.strvalue, talloc_array_length(data.strvalue) - 1);
-                       } else {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, data.strvalue, talloc_array_length(data.strvalue) - 1);
-                       }
-                       talloc_free(data.ptr);
-               } else {
-                       if (*p) {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_XLAT, name, -1);
-                       } else {
-                               vpt = tmpl_alloc(ctx, TMPL_TYPE_LITERAL, name, -1);
-                       }
-               }
-               slen = vpt->len;
-               break;
-
-       case T_BACK_QUOTED_STRING:
-               if (cf_new_escape) {
-                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '`');
-                       rad_assert(slen >= 0);
-
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, data.strvalue, talloc_array_length(data.strvalue) - 1);
-                       talloc_free(data.ptr);
-               } else {
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_EXEC, name, -1);
-               }
-               slen = vpt->len;
-               break;
-
-       case T_OP_REG_EQ: /* hack */
-               if (cf_new_escape) {
-                       slen = value_data_from_str(ctx, &data, &data_type, NULL, name, inlen, '\0'); /* no unescaping */
-                       rad_assert(slen >= 0);
-
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX, data.strvalue, talloc_array_length(data.strvalue) - 1);
-                       talloc_free(data.ptr);
-               } else {
-                       vpt = tmpl_alloc(ctx, TMPL_TYPE_REGEX, name, -1);
-               }
-               slen = vpt->len;
-               break;
-
-       default:
-               rad_assert(0);
-               return 0;       /* 0 is an error here too */
-       }
-
-       rad_assert((slen >= 0) && (vpt != NULL));
-
-       VERIFY_TMPL(vpt);
-
-       *out = vpt;
-
-       return slen;
-}
-
-/** Convert a tmpl containing literal data, to the type specified by da.
- *
- * @param[in,out] vpt the template to modify
- * @param[in] type to cast to.
- * @param[in] enumv Enumerated dictionary values.
- * @return true for success, false for failure.
- */
-bool tmpl_cast_in_place(value_pair_tmpl_t *vpt, PW_TYPE type, DICT_ATTR const *enumv)
-{
-       ssize_t ret;
-
-       VERIFY_TMPL(vpt);
-
-       rad_assert(vpt != NULL);
-       rad_assert(vpt->type == TMPL_TYPE_LITERAL);
-
-       vpt->tmpl_data_type = type;
-
-       /*
-        *      Why do we pass a pointer to the tmpl type? Goddamn WiMAX.
-        */
-       ret = value_data_from_str(vpt, &vpt->tmpl_data_value, &vpt->tmpl_data_type, enumv, vpt->name, vpt->len, '\0');
-       if (ret < 0) return false;
-
-       vpt->type = TMPL_TYPE_DATA;
-       vpt->tmpl_data_length = (size_t) ret;
-
-       VERIFY_TMPL(vpt);
-
-       return true;
-}
-
-/** Convert a tmpl of TMPL_TYPE_LITERAL to TMPL_TYPE_DATA
- *
- */
-void tmpl_cast_in_place_str(value_pair_tmpl_t *vpt)
-{
-       rad_assert(vpt != NULL);
-       rad_assert(vpt->type == TMPL_TYPE_LITERAL);
-
-       vpt->tmpl_data.vp_strvalue = talloc_typed_strdup(vpt, vpt->name);
-       rad_assert(vpt->tmpl_data.vp_strvalue != NULL);
-
-       vpt->type = TMPL_TYPE_DATA;
-       vpt->tmpl_data_type = PW_TYPE_STRING;
-       vpt->tmpl_data_length = talloc_array_length(vpt->tmpl_data.vp_strvalue) - 1;
-}
-
-/** Expand a template to a string, parse it as type of "cast", and create a VP from the data.
- */
-int tmpl_cast_to_vp(VALUE_PAIR **out, REQUEST *request,
-                   value_pair_tmpl_t const *vpt, DICT_ATTR const *cast)
-{
-       int rcode;
-       VALUE_PAIR *vp;
-       value_data_t data;
-       char *p;
-
-       VERIFY_TMPL(vpt);
-
-       *out = NULL;
-
-       vp = pairalloc(request, cast);
-       if (!vp) return -1;
-
-       if (vpt->type == TMPL_TYPE_DATA) {
-               VERIFY_VP(vp);
-               rad_assert(vp->da->type == vpt->tmpl_data_type);
-
-               value_data_copy(vp, &vp->data, vpt->tmpl_data_type, &vpt->tmpl_data_value, vpt->tmpl_data_length);
-               *out = vp;
-               return 0;
-       }
-
-       rcode = tmpl_aexpand(vp, &p, request, vpt, NULL, NULL);
-       if (rcode < 0) {
-               pairfree(&vp);
-               return rcode;
-       }
-       data.strvalue = p;
-
-       /*
-        *      New escapes: strings are in binary form.
-        */
-       if (cf_new_escape && (vp->da->type == PW_TYPE_STRING)) {
-               vp->data.ptr = talloc_steal(vp, data.ptr);
-               vp->vp_length = rcode;
-
-       } else if (pairparsevalue(vp, data.strvalue, rcode) < 0) {
-               talloc_free(data.ptr);
-               pairfree(&vp);
-               return -1;
-       }
-
-       *out = vp;
-       return 0;
-}
-
-/** Expand a template to a string, writing the result
- *
- * @param out Where to write a pointer to the string buffer.
- *     On return may point to buff if buff was used to store the value.
- *     Otherwise will point to a value_data_t buffer, or the name of
- *     the template. To force copying the value to the buffer, out
- *     should be NULL.
- * @param buff Expansion buffer, may be NULL if out is not NULL, and
- *     processing TMPL_TYPE_LITERAL or string types.
- * @param bufflen Length of expansion buffer.
- * @param request Current request.
- * @param vpt to evaluate.
- * @param escape xlat escape function (only used for xlat types).
- * @param escape_ctx xlat escape function data.
- * @return -1 on error, else 0.
- */
-ssize_t tmpl_expand(char const **out, char *buff, size_t bufflen, REQUEST *request,
-                   value_pair_tmpl_t const *vpt, RADIUS_ESCAPE_STRING escape, void *escape_ctx)
-{
-       VALUE_PAIR *vp;
-       ssize_t slen = -1;      /* quiet compiler */
-
-       rad_assert(vpt->type != TMPL_TYPE_LIST);
-
-       VERIFY_TMPL(vpt);
-
-       if (out) *out = NULL;
+       *out = NULL;
 
        switch (vpt->type) {
        case TMPL_TYPE_LITERAL:
                RDEBUG4("EXPAND TMPL LITERAL");
-
-               if (!out) {
-                       rad_assert(buff);
-                       memcpy(buff, vpt->name, vpt->len >= bufflen ? bufflen : vpt->len + 1);
-               } else {
-                       *out = vpt->name;
-               }
+               *out = talloc_bstrndup(ctx, vpt->name, vpt->len);
                return vpt->len;
 
        case TMPL_TYPE_EXEC:
        {
+               char *buff = NULL;
+
                RDEBUG4("EXPAND TMPL EXEC");
-               rad_assert(buff);
-               if (radius_exec_program(buff, bufflen, NULL, request, vpt->name, NULL,
+               buff = talloc_array(ctx, char, 1024);
+               if (radius_exec_program(request, buff, 1024, NULL, request, vpt->name, NULL,
                                        true, false, EXEC_TIMEOUT) != 0) {
+                       TALLOC_FREE(buff);
                        return -1;
                }
                slen = strlen(buff);
-               if (out) *out = buff;
+               *out = buff;
        }
                break;
 
        case TMPL_TYPE_XLAT:
                RDEBUG4("EXPAND TMPL XLAT");
-               rad_assert(buff);
                /* Error in expansion, this is distinct from zero length expansion */
-               slen = radius_xlat(buff, bufflen, request, vpt->name, escape, escape_ctx);
-               if (slen < 0) return slen;
-               if (out) *out = buff;
+               slen = radius_axlat(out, request, vpt->name, escape, escape_ctx);
+               if (slen < 0) {
+                       rad_assert(!*out);
+                       return slen;
+               }
+               rad_assert(*out);
+               slen = strlen(*out);
                break;
 
        case TMPL_TYPE_XLAT_STRUCT:
                RDEBUG4("EXPAND TMPL XLAT STRUCT");
-               rad_assert(buff);
                /* Error in expansion, this is distinct from zero length expansion */
-               slen = radius_xlat_struct(buff, bufflen, request, vpt->tmpl_xlat, escape, escape_ctx);
+               slen = radius_axlat_struct(out, request, vpt->tmpl_xlat, escape, escape_ctx);
                if (slen < 0) {
+                       rad_assert(!*out);
                        return slen;
                }
-               slen = strlen(buff);
-               if (out) *out = buff;
+               slen = strlen(*out);
                break;
 
        case TMPL_TYPE_ATTR:
@@ -1550,16 +1524,53 @@ ssize_t tmpl_expand(char const **out, char *buff, size_t bufflen, REQUEST *reque
                int ret;
 
                RDEBUG4("EXPAND TMPL ATTR");
-               rad_assert(buff);
                ret = tmpl_find_vp(&vp, request, vpt);
                if (ret < 0) return -2;
 
-               if (out && (vp->da->type == PW_TYPE_STRING)) {
-                       *out = vp->vp_strvalue;
+               switch (vpt->tmpl_da->type) {
+               case PW_TYPE_STRING:
+                       *out = talloc_bstrndup(ctx, vp->vp_strvalue, vp->vp_length);
+                       if (!*out) return -1;
                        slen = vp->vp_length;
-               } else {
-                       if (out) *out = buff;
-                       slen = vp_prints_value(buff, bufflen, vp, '\0');
+                       break;
+
+               case PW_TYPE_OCTETS:
+                       *out = talloc_memdup(ctx, vp->vp_octets, vp->vp_length);
+                       if (!*out) return -1;
+                       slen = vp->vp_length;
+                       break;
+
+               default:
+                       *out = vp_aprints_value(ctx, vp, '\0');
+                       if (!*out) return -1;
+                       slen = talloc_array_length(*out) - 1;
+                       break;
+               }
+       }
+               break;
+
+       case TMPL_TYPE_DATA:
+       {
+               RDEBUG4("EXPAND TMPL DATA");
+
+               switch (vpt->tmpl_data_type) {
+               case PW_TYPE_STRING:
+                       *out = talloc_bstrndup(ctx, vpt->tmpl_data_value.strvalue, vpt->tmpl_data_length);
+                       if (!*out) return -1;
+                       slen = vpt->tmpl_data_length;
+                       break;
+
+               case PW_TYPE_OCTETS:
+                       *out = talloc_memdup(ctx, vpt->tmpl_data_value.octets, vpt->tmpl_data_length);
+                       if (!*out) return -1;
+                       slen = vpt->tmpl_data_length;
+                       break;
+
+               default:
+                       *out = value_data_aprints(ctx, vpt->tmpl_data_type, NULL, &vpt->tmpl_data_value, vpt->tmpl_data_length, '\0');
+                       if (!*out) return -1;
+                       slen = talloc_array_length(*out) - 1;
+                       break;
                }
        }
                break;
@@ -1570,7 +1581,6 @@ ssize_t tmpl_expand(char const **out, char *buff, size_t bufflen, REQUEST *reque
        case TMPL_TYPE_UNKNOWN:
        case TMPL_TYPE_NULL:
        case TMPL_TYPE_LIST:
-       case TMPL_TYPE_DATA:
        case TMPL_TYPE_REGEX:
        case TMPL_TYPE_ATTR_UNDEFINED:
        case TMPL_TYPE_REGEX_STRUCT:
@@ -1581,18 +1591,14 @@ ssize_t tmpl_expand(char const **out, char *buff, size_t bufflen, REQUEST *reque
 
        if (slen < 0) return slen;
 
-
-#if 0
        /*
         *      If we're doing correct escapes, we may have to re-parse the string.
         *      If the string is from another expansion, it needs re-parsing.
         *      Or, if it's from a "string" attribute, it needs re-parsing.
         *      Integers, IP addresses, etc. don't need re-parsing.
         */
-       if (cf_new_escape &&
-           ((vpt->type != TMPL_TYPE_ATTR) ||
-            (vpt->tmpl_da->type == PW_TYPE_STRING))) {
-               value_data_t vd;
+       if (cf_new_escape && (vpt->type != TMPL_TYPE_ATTR)) {
+               value_data_t    vd;
 
                PW_TYPE type = PW_TYPE_STRING;
 
@@ -1600,157 +1606,235 @@ ssize_t tmpl_expand(char const **out, char *buff, size_t bufflen, REQUEST *reque
                talloc_free(*out);      /* free the old value */
                *out = vd.ptr;
        }
-#endif
 
        if (vpt->type == TMPL_TYPE_XLAT_STRUCT) {
                RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
-               RDEBUG2("   --> %s", buff);
+               RDEBUG2("   --> %s", *out);
        }
 
        return slen;
 }
 
-/** Expand a template to a string, writing the result
+/** Print a #vp_tmpl_t to a string
  *
- * @param ctx to alloc output string in.
- * @param out Result of expanding the tmpl.
- * @param request Current request.
- * @param vpt to evaluate.
- * @param escape xlat escape function (only used for xlat types).
- * @param escape_ctx xlat escape function data.
- * @return -1 on error, else 0.
+ * @param[out] out Where to write the presentation format #vp_tmpl_t string.
+ * @param[in] outlen Size of output buffer.
+ * @param[in] vpt to print
+ * @param[in] values Used for integer attributes only. #DICT_ATTR to use when mapping integer
+ *     values to strings.
+ * @return the size of the string written to the output buffer.
  */
-ssize_t tmpl_aexpand(TALLOC_CTX *ctx, char **out, REQUEST *request, value_pair_tmpl_t const *vpt,
-                    RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+size_t tmpl_prints(char *out, size_t outlen, vp_tmpl_t const *vpt, DICT_ATTR const *values)
 {
-       VALUE_PAIR *vp;
-       ssize_t slen = -1;      /* quiet compiler */
+       size_t len;
+       char c;
+       char const *p;
+       char *q = out;
 
-       rad_assert(vpt->type != TMPL_TYPE_LIST);
+       if (!vpt) {
+               *out = '\0';
+               return 0;
+       }
 
        VERIFY_TMPL(vpt);
 
-       *out = NULL;
-
        switch (vpt->type) {
-       case TMPL_TYPE_LITERAL:
-               RDEBUG4("EXPAND TMPL LITERAL");
-               *out = talloc_memdup(ctx, vpt->name, vpt->len);
-               return vpt->len;
-
-       case TMPL_TYPE_EXEC:
-       {
-               char *buff = NULL;
+       default:
+               return 0;
 
-               RDEBUG4("EXPAND TMPL EXEC");
-               buff = talloc_array(ctx, char, 1024);
-               if (radius_exec_program(buff, 1024, NULL, request, vpt->name, NULL, true, false, EXEC_TIMEOUT) != 0) {
-                       TALLOC_FREE(buff);
-                       return -1;
-               }
-               slen = strlen(buff);
-               *out = buff;
-       }
+       case TMPL_TYPE_REGEX:
+       case TMPL_TYPE_REGEX_STRUCT:
+               c = '/';
                break;
 
        case TMPL_TYPE_XLAT:
-               RDEBUG4("EXPAND TMPL XLAT");
-               /* Error in expansion, this is distinct from zero length expansion */
-               slen = radius_axlat(out, request, vpt->name, escape, escape_ctx);
-               if (slen < 0) {
-                       rad_assert(!*out);
-                       return slen;
-               }
-               rad_assert(*out);
-               slen = strlen(*out);
+       case TMPL_TYPE_XLAT_STRUCT:
+               c = '"';
                break;
+       case TMPL_TYPE_LITERAL: /* single-quoted or bare word */
+               /*
+                *      Hack
+                */
+               for (p = vpt->name; *p != '\0'; p++) {
+                       if (*p == ' ') break;
+                       if (*p == '\'') break;
+                       if (!dict_attr_allowed_chars[(int) *p]) break;
+               }
 
-       case TMPL_TYPE_XLAT_STRUCT:
-               RDEBUG4("EXPAND TMPL XLAT STRUCT");
-               /* Error in expansion, this is distinct from zero length expansion */
-               slen = radius_axlat_struct(out, request, vpt->tmpl_xlat, escape, escape_ctx);
-               if (slen < 0) {
-                       rad_assert(!*out);
-                       return slen;
+               if (!*p) {
+                       strlcpy(out, vpt->name, outlen);
+                       return strlen(out);
                }
-               slen = strlen(*out);
+
+               c = vpt->quote;
                break;
 
+       case TMPL_TYPE_EXEC:
+               c = '`';
+               break;
+
+       case TMPL_TYPE_LIST:
+               out[0] = '&';
+               if (vpt->tmpl_request == REQUEST_CURRENT) {
+                       snprintf(out + 1, outlen - 1, "%s",
+                                fr_int2str(pair_lists, vpt->tmpl_list, ""));
+               } else {
+                       snprintf(out + 1, outlen - 1, "%s.%s",
+                                fr_int2str(request_refs, vpt->tmpl_request, ""),
+                                fr_int2str(pair_lists, vpt->tmpl_list, ""));
+               }
+               len = strlen(out);
+               goto attr_inst_tag;
+
        case TMPL_TYPE_ATTR:
-       {
-               int ret;
+               out[0] = '&';
+               if (vpt->tmpl_request == REQUEST_CURRENT) {
+                       if (vpt->tmpl_list == PAIR_LIST_REQUEST) {
+                               strlcpy(out + 1, vpt->tmpl_da->name, outlen - 1);
+                       } else {
+                               snprintf(out + 1, outlen - 1, "%s:%s",
+                                        fr_int2str(pair_lists, vpt->tmpl_list, ""),
+                                        vpt->tmpl_da->name);
+                       }
 
-               RDEBUG4("EXPAND TMPL ATTR");
-               ret = tmpl_find_vp(&vp, request, vpt);
-               if (ret < 0) return -2;
+               } else {
+                       snprintf(out + 1, outlen - 1, "%s.%s:%s",
+                                fr_int2str(request_refs, vpt->tmpl_request, ""),
+                                fr_int2str(pair_lists, vpt->tmpl_list, ""),
+                                vpt->tmpl_da->name);
+               }
 
-               *out = vp_aprints_value(ctx, vp, '"');
-               if (!*out) return -1;
-               slen = talloc_array_length(*out) - 1;
-       }
-               break;
+               len = strlen(out);
+
+       attr_inst_tag:
+               if ((vpt->tmpl_tag == TAG_ANY) && (vpt->tmpl_num == NUM_ANY)) return len;
+
+               q = out + len;
+               outlen -= len;
+
+               if (vpt->tmpl_tag != TAG_ANY) {
+                       snprintf(q, outlen, ":%d", vpt->tmpl_tag);
+                       len = strlen(q);
+                       q += len;
+                       outlen -= len;
+               }
+
+               switch (vpt->tmpl_num) {
+               case NUM_ANY:
+                       break;
+
+               case NUM_ALL:
+                       snprintf(q, outlen, "[*]");
+                       len = strlen(q);
+                       q += len;
+                       break;
+
+               case NUM_COUNT:
+                       snprintf(q, outlen, "[#]");
+                       len = strlen(q);
+                       q += len;
+                       break;
+
+               case NUM_LAST:
+                       snprintf(q, outlen, "[n]");
+                       len = strlen(q);
+                       q += len;
+                       break;
+
+               default:
+                       snprintf(q, outlen, "[%i]", vpt->tmpl_num);
+                       len = strlen(q);
+                       q += len;
+                       break;
+               }
+
+               return (q - out);
 
-       /*
-        *      We should never be expanding these.
-        */
-       case TMPL_TYPE_UNKNOWN:
-       case TMPL_TYPE_NULL:
-       case TMPL_TYPE_LIST:
-       case TMPL_TYPE_DATA:
-       case TMPL_TYPE_REGEX:
        case TMPL_TYPE_ATTR_UNDEFINED:
-       case TMPL_TYPE_REGEX_STRUCT:
-               rad_assert(0 == 1);
-               slen = -1;
-               break;
-       }
+               out[0] = '&';
+               if (vpt->tmpl_request == REQUEST_CURRENT) {
+                       if (vpt->tmpl_list == PAIR_LIST_REQUEST) {
+                               strlcpy(out + 1, vpt->tmpl_unknown_name, outlen - 1);
+                       } else {
+                               snprintf(out + 1, outlen - 1, "%s:%s",
+                                        fr_int2str(pair_lists, vpt->tmpl_list, ""),
+                                        vpt->tmpl_unknown_name);
+                       }
 
-       if (slen < 0) return slen;
+               } else {
+                       snprintf(out + 1, outlen - 1, "%s.%s:%s",
+                                fr_int2str(request_refs, vpt->tmpl_request, ""),
+                                fr_int2str(pair_lists, vpt->tmpl_list, ""),
+                                vpt->tmpl_unknown_name);
+               }
 
-       /*
-        *      If we're doing correct escapes, we may have to re-parse the string.
-        *      If the string is from another expansion, it needs re-parsing.
-        *      Or, if it's from a "string" attribute, it needs re-parsing.
-        *      Integers, IP addresses, etc. don't need re-parsing.
-        */
-       if (cf_new_escape &&
-           ((vpt->type != TMPL_TYPE_ATTR) ||
-            (vpt->tmpl_da->type == PW_TYPE_STRING))) {
-               value_data_t vd;
+               len = strlen(out);
 
-               PW_TYPE type = PW_TYPE_STRING;
+               if (vpt->tmpl_num == NUM_ANY) {
+                       return len;
+               }
 
-               slen = value_data_from_str(ctx, &vd, &type, NULL, *out, slen, '"');
-               talloc_free(*out);      /* free the old value */
-               *out = vd.ptr;
+               q = out + len;
+               outlen -= len;
+
+               if (vpt->tmpl_num != NUM_ANY) {
+                       snprintf(q, outlen, "[%i]", vpt->tmpl_num);
+                       len = strlen(q);
+                       q += len;
+               }
+
+               return (q - out);
+
+       case TMPL_TYPE_DATA:
+               return value_data_prints(out, outlen, vpt->tmpl_data_type, values, &vpt->tmpl_data_value,
+                                        vpt->tmpl_data_length, vpt->quote);
        }
 
-       if (vpt->type == TMPL_TYPE_XLAT_STRUCT) {
-               RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
-               RDEBUG2("   --> %s", *out);
+       if (outlen <= 3) {
+               *out = '\0';
+               return 0;
        }
 
-       return slen;
+       *(q++) = c;
+
+       /*
+        *      Print it with appropriate escaping
+        */
+       if (cf_new_escape && (c == '/')) {
+               len = fr_prints(q, outlen - 3, vpt->name, vpt->len, '\0');
+       } else {
+               len = fr_prints(q, outlen - 3, vpt->name, vpt->len, c);
+       }
+
+       q += len;
+       *(q++) = c;
+       *q = '\0';
+
+       return q - out;
 }
 
-/** Initialise a vp_cursor_t to the VALUE_PAIR specified by a value_pair_tmpl_t
+/** Initialise a #vp_cursor_t to the #VALUE_PAIR specified by a #vp_tmpl_t
  *
- * This makes iterating over the one or more VALUE_PAIRs specified by a value_pair_tmpl_t
+ * This makes iterating over the one or more #VALUE_PAIR specified by a #vp_tmpl_t
  * significantly easier.
  *
- * @see tmpl_cursor_next
- *
- * @param err Will be set to -1 if VP could not be found, -2 if list could not be found,
- *     -3 if context could not be found and NULL will be returned. Will be 0 on success.
+ * @param err May be NULL if no error code is required. Will be set to:
+ *     - 0 on success.
+ *     - -1 if no matching #VALUE_PAIR could be found.
+ *     - -2 if list could not be found (doesn't exist in current #REQUEST).
+ *     - -3 if context could not be found (no parent #REQUEST available).
  * @param cursor to store iterator state.
- * @param request The current request.
- * @param vpt specifying the VALUE_PAIRs to iterate over.
- * @return the first VALUE_PAIR specified by the value_pair_tmpl_t, NULL if no matching VALUE_PAIRs exist,
- *     and NULL on error.
+ * @param request The current #REQUEST.
+ * @param vpt specifying the #VALUE_PAIR type/tag or list to iterate over.
+ * @return the first #VALUE_PAIR specified by the #vp_tmpl_t, or NULL if no matching
+ *     #VALUE_PAIR found, and NULL on error.
+ *
+ * @see tmpl_cursor_next
  */
-VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, value_pair_tmpl_t const *vpt)
+VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, vp_tmpl_t const *vpt)
 {
        VALUE_PAIR **vps, *vp = NULL;
+       int num;
 
        VERIFY_TMPL(vpt);
 
@@ -1774,9 +1858,6 @@ VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, va
         *      May not may not be found, but it *is* a known name.
         */
        case TMPL_TYPE_ATTR:
-       {
-               int num;
-
                switch (vpt->tmpl_num) {
                case NUM_ANY:
                        vp = fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag);
@@ -1791,7 +1872,6 @@ VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, va
                 *      Get the last instance of a VALUE_PAIR.
                 */
                case NUM_LAST:
-
                {
                        VALUE_PAIR *last = NULL;
 
@@ -1799,7 +1879,7 @@ VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, va
                                VERIFY_VP(vp);
                                last = vp;
                        }
-
+                       VERIFY_VP(last);
                        if (!last) break;
                        return last;
                }
@@ -1813,6 +1893,8 @@ VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, va
                 *      total number of attributes.
                 */
                case NUM_COUNT:
+                       return fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag);
+
                default:
                        num = vpt->tmpl_num;
                        while ((vp = fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag))) {
@@ -1824,10 +1906,49 @@ VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, va
 
                if (err) *err = -1;
                return NULL;
-       }
 
        case TMPL_TYPE_LIST:
-               vp = fr_cursor_init(cursor, vps);
+               switch (vpt->tmpl_num) {
+               case NUM_COUNT:
+               case NUM_ANY:
+               case NUM_ALL:
+                       vp = fr_cursor_init(cursor, vps);
+                       if (!vp) {
+                               if (err) *err = -1;
+                               return NULL;
+                       }
+                       VERIFY_VP(vp);
+                       return vp;
+
+               /*
+                *      Get the last instance of a VALUE_PAIR.
+                */
+               case NUM_LAST:
+               {
+                       VALUE_PAIR *last = NULL;
+
+                       for (vp = fr_cursor_init(cursor, vps);
+                            vp;
+                            vp = fr_cursor_next(cursor)) {
+                               VERIFY_VP(vp);
+                               last = vp;
+                       }
+                       if (!last) break;
+                       VERIFY_VP(last);
+                       return last;
+               }
+
+               default:
+                       num = vpt->tmpl_num;
+                       for (vp = fr_cursor_init(cursor, vps);
+                            vp;
+                            vp = fr_cursor_next(cursor)) {
+                               VERIFY_VP(vp);
+                               if (num-- <= 0) return vp;
+                       }
+                       break;
+               }
+
                break;
 
        default:
@@ -1837,15 +1958,16 @@ VALUE_PAIR *tmpl_cursor_init(int *err, vp_cursor_t *cursor, REQUEST *request, va
        return vp;
 }
 
-/** Gets the next VALUE_PAIR specified by value_pair_tmpl_t
- *
- * Returns the next VALUE_PAIR matching a value_pair_tmpl_t
+/** Returns the next #VALUE_PAIR specified by vpt
  *
- * @param cursor initialised with tmpl_cursor_init.
- * @param vpt specifying the VALUE_PAIRs to iterate over.
- * @return NULL if no more matching VALUE_PAIRs found.
+ * @param cursor initialised with #tmpl_cursor_init.
+ * @param vpt specifying the #VALUE_PAIR type/tag to iterate over.
+ *     Must be one of the following types:
+ *     - #TMPL_TYPE_LIST
+ *     - #TMPL_TYPE_ATTR
+ * @return NULL if no more matching #VALUE_PAIR of the specified type/tag are found.
  */
-VALUE_PAIR *tmpl_cursor_next(vp_cursor_t *cursor, value_pair_tmpl_t const *vpt)
+VALUE_PAIR *tmpl_cursor_next(vp_cursor_t *cursor, vp_tmpl_t const *vpt)
 {
        rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));
 
@@ -1856,10 +1978,25 @@ VALUE_PAIR *tmpl_cursor_next(vp_cursor_t *cursor, value_pair_tmpl_t const *vpt)
         *      May not may not be found, but it *is* a known name.
         */
        case TMPL_TYPE_ATTR:
-               if (vpt->tmpl_num != NUM_ALL) return NULL;
+               switch (vpt->tmpl_num) {
+               default:
+                       return NULL;
+
+               case NUM_ALL:
+               case NUM_COUNT: /* This cursor is being used to count matching attrs */
+                       break;
+               }
                return fr_cursor_next_by_da(cursor, vpt->tmpl_da, vpt->tmpl_tag);
 
        case TMPL_TYPE_LIST:
+               switch (vpt->tmpl_num) {
+               default:
+                       return NULL;
+
+               case NUM_ALL:
+               case NUM_COUNT: /* This cursor is being used to count matching attrs */
+                       break;
+               }
                return fr_cursor_next(cursor);
 
        default:
@@ -1868,16 +2005,22 @@ VALUE_PAIR *tmpl_cursor_next(vp_cursor_t *cursor, value_pair_tmpl_t const *vpt)
        }
 }
 
-/** Copy pairs matching a VPT in the current request
+/** Copy pairs matching a #vp_tmpl_t in the current #REQUEST
  *
- * @param ctx to allocate new VALUE_PAIRs under.
- * @param out Where to write the copied vps.
- * @param request current request.
- * @param vpt specifying the VALUE_PAIRs to iterate over.
- * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found,
- *     -4 on memory allocation error.
+ * @param ctx to allocate new #VALUE_PAIR in.
+ * @param out Where to write the copied #VALUE_PAIR (s).
+ * @param request The current #REQUEST.
+ * @param vpt specifying the #VALUE_PAIR type/tag or list to copy.
+ *     Must be one of the following types:
+ *     - #TMPL_TYPE_LIST
+ *     - #TMPL_TYPE_ATTR
+ * @return
+ *     - -1 if no matching #VALUE_PAIR could be found.
+ *     - -2 if list could not be found (doesn't exist in current #REQUEST).
+ *     - -3 if context could not be found (no parent #REQUEST available).
+ *     - -4 on memory allocation error.
  */
-int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
+int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_tmpl_t const *vpt)
 {
        VALUE_PAIR *vp;
        vp_cursor_t from, to;
@@ -1895,9 +2038,9 @@ int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pai
        for (vp = tmpl_cursor_init(&err, &from, request, vpt);
             vp;
             vp = tmpl_cursor_next(&from, vpt)) {
-               vp = paircopyvp(ctx, vp);
+               vp = fr_pair_copy(ctx, vp);
                if (!vp) {
-                       pairfree(out);
+                       fr_pair_list_free(out);
                        return -4;
                }
                fr_cursor_insert(&to, vp);
@@ -1906,14 +2049,20 @@ int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pai
        return err;
 }
 
-/** Gets the first VP from a value_pair_tmpl_t
+/** Returns the first VP matching a #vp_tmpl_t
  *
  * @param out where to write the retrieved vp.
- * @param request current request.
- * @param vpt specifying the VALUE_PAIRs to iterate over.
- * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
+ * @param request The current #REQUEST.
+ * @param vpt specifying the #VALUE_PAIR type/tag to find.
+ *     Must be one of the following types:
+ *     - #TMPL_TYPE_LIST
+ *     - #TMPL_TYPE_ATTR
+ * @return
+ *     - -1 if no matching #VALUE_PAIR could be found.
+ *     - -2 if list could not be found (doesn't exist in current #REQUEST).
+ *     - -3 if context could not be found (no parent #REQUEST available).
  */
-int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
+int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, vp_tmpl_t const *vpt)
 {
        vp_cursor_t cursor;
        VALUE_PAIR *vp;
@@ -1927,24 +2076,315 @@ int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vp
 
        return err;
 }
+/* @} **/
 
-bool tmpl_define_unknown_attr(value_pair_tmpl_t *vpt)
+#ifdef WITH_VERIFY_PTR
+/** Used to check whether areas of a vp_tmpl_t are zeroed out
+ *
+ * @param ptr Offset to begin checking at.
+ * @param len How many bytes to check.
+ * @return pointer to the first non-zero byte, or NULL if all bytes were zero.
+ */
+static uint8_t const *not_zeroed(uint8_t const *ptr, size_t len)
 {
-       DICT_ATTR const *da;
+       size_t i;
 
-       if (!vpt) return false;
+       for (i = 0; i < len; i++) {
+               if (ptr[i] != 0x00) return ptr + i;
+       }
 
-       VERIFY_TMPL(vpt);
+       return NULL;
+}
+#define CHECK_ZEROED(_x) not_zeroed((uint8_t const *)&_x + sizeof(_x), sizeof(vpt->data) - sizeof(_x))
 
-       if ((vpt->type != TMPL_TYPE_ATTR) &&
-           (vpt->type != TMPL_TYPE_DATA)) {
-               return true;
+/** Verify fields of a vp_tmpl_t make sense
+ *
+ * @note If the #vp_tmpl_t is invalid, causes the server to exit.
+ *
+ * @param file obtained with __FILE__.
+ * @param line obtained with __LINE__.
+ * @param vpt to check.
+ */
+void tmpl_verify(char const *file, int line, vp_tmpl_t const *vpt)
+{
+       rad_assert(vpt);
+
+       if (vpt->type == TMPL_TYPE_UNKNOWN) {
+               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: vp_tmpl_t type was "
+                            "TMPL_TYPE_UNKNOWN (uninitialised)", file, line);
+               fr_assert(0);
+               fr_exit_now(1);
        }
 
-       if (!vpt->tmpl_da->flags.is_unknown) return true;
+       if (vpt->type > TMPL_TYPE_NULL) {
+               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: vp_tmpl_t type was %i "
+                            "(outside range of tmpl_names)", file, line, vpt->type);
+               fr_assert(0);
+               fr_exit_now(1);
+       }
 
-       da = dict_unknown_add(vpt->tmpl_da);
-       if (!da) return false;
-       vpt->tmpl_da = da;
-       return true;
+       /*
+        *  Do a memcmp of the bytes after where the space allocated for
+        *  the union member should have ended and the end of the union.
+        *  These should always be zero if the union has been initialised
+        *  properly.
+        *
+        *  If they're still all zero, do TMPL_TYPE specific checks.
+        */
+       switch (vpt->type) {
+       case TMPL_TYPE_NULL:
+               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_NULL "
+                                    "has non-zero bytes in its data union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               break;
+
+       case TMPL_TYPE_LITERAL:
+               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LITERAL "
+                                    "has non-zero bytes in its data union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               break;
+
+       case TMPL_TYPE_XLAT:
+       case TMPL_TYPE_XLAT_STRUCT:
+               break;
+
+/* @todo When regexes get converted to xlat the flags field of the regex union is used
+       case TMPL_TYPE_XLAT:
+               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT "
+                                    "has non-zero bytes in its data union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               break;
+
+       case TMPL_TYPE_XLAT_STRUCT:
+               if (CHECK_ZEROED(vpt->data.xlat)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_XLAT_STRUCT "
+                                    "has non-zero bytes after the data.xlat pointer in the union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               break;
+*/
+
+       case TMPL_TYPE_EXEC:
+               if (not_zeroed((uint8_t const *)&vpt->data, sizeof(vpt->data))) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_EXEC "
+                                    "has non-zero bytes in its data union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               break;
+
+       case TMPL_TYPE_ATTR_UNDEFINED:
+               rad_assert(vpt->tmpl_da == NULL);
+               break;
+
+       case TMPL_TYPE_ATTR:
+               if (CHECK_ZEROED(vpt->data.attribute)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
+                                    "has non-zero bytes after the data.attribute struct in the union",
+                                    file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (vpt->tmpl_da->flags.is_unknown) {
+                       if (vpt->tmpl_da != (DICT_ATTR const *)&vpt->data.attribute.unknown.da) {
+                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
+                                            "da is marked as unknown, but does not point to the template's "
+                                            "unknown da buffer", file, line);
+                               fr_assert(0);
+                               fr_exit_now(1);
+                       }
+
+               } else {
+                       DICT_ATTR const *da;
+
+                       /*
+                        *      Attribute may be present with multiple names
+                        */
+                       da = dict_attrbyname(vpt->tmpl_da->name);
+                       if (!da) {
+                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
+                                            "attribute \"%s\" (%s) not found in global dictionary",
+                                            file, line, vpt->tmpl_da->name,
+                                            fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"));
+                               fr_assert(0);
+                               fr_exit_now(1);
+                       }
+
+                       if ((da->type == PW_TYPE_COMBO_IP_ADDR) && (da->type != vpt->tmpl_da->type)) {
+                               da = dict_attrbytype(vpt->tmpl_da->attr, vpt->tmpl_da->vendor, vpt->tmpl_da->type);
+                               if (!da) {
+                                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
+                                                    "attribute \"%s\" variant (%s) not found in global dictionary",
+                                                    file, line, vpt->tmpl_da->name,
+                                                    fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"));
+                                       fr_assert(0);
+                                       fr_exit_now(1);
+                               }
+                       }
+
+                       if (da != vpt->tmpl_da) {
+                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_ATTR "
+                                            "dictionary pointer %p \"%s\" (%s) "
+                                            "and global dictionary pointer %p \"%s\" (%s) differ",
+                                            file, line,
+                                            vpt->tmpl_da, vpt->tmpl_da->name,
+                                            fr_int2str(dict_attr_types, vpt->tmpl_da->type, "<INVALID>"),
+                                            da, da->name,
+                                            fr_int2str(dict_attr_types, da->type, "<INVALID>"));
+                               fr_assert(0);
+                               fr_exit_now(1);
+                       }
+               }
+               break;
+
+       case TMPL_TYPE_LIST:
+               if (CHECK_ZEROED(vpt->data.attribute)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST"
+                                    "has non-zero bytes after the data.attribute struct in the union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (vpt->tmpl_da != NULL) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_LIST da pointer was NULL", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               break;
+
+       case TMPL_TYPE_DATA:
+               if (CHECK_ZEROED(vpt->data.literal)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA "
+                                    "has non-zero bytes after the data.literal struct in the union",
+                                    file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (vpt->tmpl_data_type == PW_TYPE_INVALID) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
+                                    "PW_TYPE_INVALID (uninitialised)", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (vpt->tmpl_data_type >= PW_TYPE_MAX) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA type was "
+                                    "%i (outside the range of PW_TYPEs)", file, line, vpt->tmpl_data_type);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               /*
+                *      Unlike VALUE_PAIRs we can't guarantee that VALUE_PAIR_TMPL buffers will
+                *      be talloced. They may be allocated on the stack or in global variables.
+                */
+               switch (vpt->tmpl_data_type) {
+               case PW_TYPE_STRING:
+               if (vpt->tmpl_data.vp_strvalue[vpt->tmpl_data_length] != '\0') {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA char buffer not \\0 "
+                                    "terminated", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+                       break;
+
+               case PW_TYPE_TLV:
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA is of type TLV",
+                                    file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+
+               case PW_TYPE_OCTETS:
+                       break;
+
+               default:
+                       if (vpt->tmpl_data_length == 0) {
+                               FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_DATA data pointer not NULL "
+                                            "but len field is zero", file, line);
+                               fr_assert(0);
+                               fr_exit_now(1);
+                       }
+               }
+
+               break;
+
+       case TMPL_TYPE_REGEX:
+               /*
+                *      iflag field is used for non compiled regexes too.
+                */
+               if (CHECK_ZEROED(vpt->data.preg)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
+                                    "has non-zero bytes after the data.preg struct in the union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (vpt->tmpl_preg != NULL) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
+                                    "preg field was not nULL", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if ((vpt->tmpl_iflag != true) && (vpt->tmpl_iflag != false)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
+                                    "iflag field was neither true or false", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if ((vpt->tmpl_mflag != true) && (vpt->tmpl_mflag != false)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
+                                    "mflag field was neither true or false", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               break;
+
+       case TMPL_TYPE_REGEX_STRUCT:
+               if (CHECK_ZEROED(vpt->data.preg)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
+                                    "has non-zero bytes after the data.preg struct in the union", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if (vpt->tmpl_preg == NULL) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
+                                    "comp field was NULL", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if ((vpt->tmpl_iflag != true) && (vpt->tmpl_iflag != false)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX_STRUCT "
+                                    "iflag field was neither true or false", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+
+               if ((vpt->tmpl_mflag != true) && (vpt->tmpl_mflag != false)) {
+                       FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: TMPL_TYPE_REGEX "
+                                    "mflag field was neither true or false", file, line);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               break;
+
+       case TMPL_TYPE_UNKNOWN:
+               rad_assert(0);
+       }
 }
+#endif
index 0f6578b..9952a8a 100644 (file)
@@ -41,8 +41,6 @@ char const *progname = NULL;
 char const *radacct_dir = NULL;
 char const *radlog_dir = NULL;
 char const *radlib_dir = NULL;
-log_lvl_t debug_flag = 0;
-bool check_config = false;
 bool log_stripped_names = false;
 
 static bool memory_report = false;
@@ -104,14 +102,17 @@ static RADCLIENT *client_alloc(void *ctx)
 
 static REQUEST *request_setup(FILE *fp)
 {
-       VALUE_PAIR *vp;
-       REQUEST *request;
-       vp_cursor_t cursor;
+       VALUE_PAIR      *vp;
+       REQUEST         *request;
+       vp_cursor_t     cursor;
+       struct timeval  now;
 
        /*
         *      Create and initialize the new request.
         */
        request = request_alloc(NULL);
+       gettimeofday(&now, NULL);
+       request->timestamp = now.tv_sec;
 
        request->packet = rad_alloc(request, false);
        if (!request->packet) {
@@ -119,6 +120,7 @@ static REQUEST *request_setup(FILE *fp)
                talloc_free(request);
                return NULL;
        }
+       request->packet->timestamp = now;
 
        request->reply = rad_alloc(request, false);
        if (!request->reply) {
@@ -142,7 +144,7 @@ static REQUEST *request_setup(FILE *fp)
        /*
         *      Read packet from fp
         */
-       if (readvp2(request->packet, &request->packet->vps, fp, &filedone) < 0) {
+       if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, fp, &filedone) < 0) {
                fr_perror("unittest");
                talloc_free(request);
                return NULL;
@@ -292,9 +294,9 @@ static REQUEST *request_setup(FILE *fp)
                        vp->da = da;
 
                        /*
-                        *      Re-do pairmemsteal ourselves,
+                        *      Re-do fr_pair_value_memsteal ourselves,
                         *      because we play games with
-                        *      vp->da, and pairmemsteal goes
+                        *      vp->da, and fr_pair_value_memsteal goes
                         *      to GREAT lengths to sanitize
                         *      and fix and change and
                         *      double-check the various
@@ -313,7 +315,7 @@ static REQUEST *request_setup(FILE *fp)
                }
        } /* loop over the VP's we read in */
 
-       if (debug_flag) {
+       if (rad_debug_lvl) {
                for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
                     vp = fr_cursor_next(&cursor)) {
@@ -351,11 +353,11 @@ static REQUEST *request_setup(FILE *fp)
        /*
         *      Debugging
         */
-       request->log.lvl = debug_flag;
+       request->log.lvl = rad_debug_lvl;
        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);
+       request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+       request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
 
        return request;
 }
@@ -428,7 +430,7 @@ static ssize_t xlat_poke(UNUSED void *instance, REQUEST *request,
 
        *(p++) = '\0';
 
-       mi = find_module_instance(modules, buffer, false);
+       mi = module_find(modules, buffer);
        if (!mi) {
                RDEBUG("Failed finding module '%s'", buffer);
        fail:
@@ -514,16 +516,22 @@ static ssize_t xlat_poke(UNUSED void *instance, REQUEST *request,
  */
 static bool do_xlats(char const *filename, FILE *fp)
 {
-       int lineno = 0;
-       ssize_t len;
-       char *p;
-       char input[8192];
-       char output[8192];
-       REQUEST *request;
+       int             lineno = 0;
+       ssize_t         len;
+       char            *p;
+       char            input[8192];
+       char            output[8192];
+       REQUEST         *request;
+       struct timeval  now;
 
+       /*
+        *      Create and initialize the new request.
+        */
        request = request_alloc(NULL);
+       gettimeofday(&now, NULL);
+       request->timestamp = now.tv_sec;
 
-       request->log.lvl = debug_flag;
+       request->log.lvl = rad_debug_lvl;
        request->log.func = vradlog_request;
 
        output[0] = '\0';
@@ -622,6 +630,9 @@ int main(int argc, char *argv[])
        VALUE_PAIR *vp;
        VALUE_PAIR *filter_vps = NULL;
        bool xlat_only = false;
+       fr_state_t *state = NULL;
+
+       fr_talloc_fault_setup();
 
        /*
         *      If the server was built with debugging enabled always install
@@ -639,7 +650,7 @@ int main(int argc, char *argv[])
        else
                progname++;
 
-       debug_flag = 0;
+       rad_debug_lvl = 0;
        set_radius_dir(NULL, RADIUS_DIR);
 
        /*
@@ -713,14 +724,14 @@ int main(int argc, char *argv[])
                                exit(EXIT_FAILURE);
 
                        case 'X':
-                               debug_flag += 2;
+                               rad_debug_lvl += 2;
                                main_config.log_auth = true;
                                main_config.log_auth_badpass = true;
                                main_config.log_auth_goodpass = true;
                                break;
 
                        case 'x':
-                               debug_flag++;
+                               rad_debug_lvl++;
                                break;
 
                        default:
@@ -729,8 +740,8 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (debug_flag) version_print();
-       fr_debug_flag = debug_flag;
+       if (rad_debug_lvl) version_print();
+       fr_debug_lvl = rad_debug_lvl;
 
        /*
         *      Mismatch between the binary and the libraries it depends on
@@ -740,6 +751,13 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
        }
 
+       /*
+        *  Initialising OpenSSL once, here, is safer than having individual modules do it.
+        */
+#ifdef HAVE_OPENSSL_CRYPTO_H
+       tls_global_init();
+#endif
+
        if (xlat_register("poke", xlat_poke, NULL, NULL) < 0) {
                rcode = EXIT_FAILURE;
                goto finish;
@@ -759,16 +777,21 @@ int main(int argc, char *argv[])
                goto finish;
        }
 
-       fr_state_init();
+       state =fr_state_init(NULL);
 
-       /* 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;
+       /*
+        *  Set the panic action (if required)
+        */
+       {
+               char const *panic_action = NULL;
+
+               panic_action = getenv("PANIC_ACTION");
+               if (!panic_action) panic_action = main_config.panic_action;
+
+               if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) {
+                       fr_perror("radiusd");
+                       exit(EXIT_FAILURE);
+               }
        }
 
        setlinebuf(stdout); /* unbuffered output */
@@ -833,7 +856,7 @@ int main(int argc, char *argv[])
                }
 
 
-               if (readvp2(request, &filter_vps, fp, &filedone) < 0) {
+               if (fr_pair_list_afrom_file(request, &filter_vps, fp, &filedone) < 0) {
                        fprintf(stderr, "Failed reading attributes from %s: %s\n",
                                filter_file, fr_strerror());
                        rcode = EXIT_FAILURE;
@@ -866,17 +889,17 @@ int main(int argc, char *argv[])
        /*
         *      Update the list with the response type.
         */
-       vp = radius_paircreate(request->reply, &request->reply->vps,
+       vp = radius_pair_create(request->reply, &request->reply->vps,
                               PW_RESPONSE_PACKET_TYPE, 0);
        vp->vp_integer = request->reply->code;
 
        {
                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);
+               if (filter_vps && !fr_pair_validate(failed, filter_vps, request->reply->vps)) {
+                       fr_pair_validate_debug(request, failed);
+                       fr_perror("Output file %s does not match attributes in filter %s (%s)",
+                                 output_file ? output_file : input_file, filter_file, fr_strerror());
                        rcode = EXIT_FAILURE;
                        goto finish;
                }
@@ -896,7 +919,7 @@ finish:
 
        xlat_free();            /* modules may have xlat's */
 
-       fr_state_delete();
+       fr_state_delete(state);
 
        /*
         *      Free the configuration items.
index 5129431..cdc6ec0 100644 (file)
@@ -6,7 +6,7 @@ SOURCES := acct.c auth.c client.c crypt.c files.c \
                  realms.c
 
 ifneq ($(OPENSSL_LIBS),)
-SOURCES        += cb.c tls.c
+SOURCES                += cb.c tls.c
 endif
 
 SRC_CFLAGS     := -DHOSTINFO=\"${HOSTINFO}\"
index 3b9386e..7b8c202 100644 (file)
@@ -277,6 +277,86 @@ int rad_mkdir(char *dir, mode_t mode, uid_t uid, gid_t gid)
        return rcode;
 }
 
+/** Ensures that a filename cannot walk up the directory structure
+ *
+ * Also sanitizes control chars.
+ *
+ * @param request Current request (may be NULL).
+ * @param out Output buffer.
+ * @param outlen Size of the output buffer.
+ * @param in string to escape.
+ * @param arg Context arguments (unused, should be NULL).
+ */
+size_t rad_filename_make_safe(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
+{
+       char const *q = in;
+       char *p = out;
+       size_t left = outlen;
+
+       while (*q) {
+               if (*q != '/') {
+                       if (left < 2) break;
+
+                       /*
+                        *      Smash control characters and spaces to
+                        *      something simpler.
+                        */
+                       if (*q < ' ') {
+                               *(p++) = '_';
+                               continue;
+                       }
+
+                       *(p++) = *(q++);
+                       left--;
+                       continue;
+               }
+
+               /*
+                *      For now, allow slashes in the expanded
+                *      filename.  This allows the admin to set
+                *      attributes which create sub-directories.
+                *      Unfortunately, it also allows users to send
+                *      attributes which *may* end up creating
+                *      sub-directories.
+                */
+               if (left < 2) break;
+               *(p++) = *(q++);
+
+               /*
+                *      Get rid of ////../.././///.///..//
+                */
+       redo:
+               /*
+                *      Get rid of ////
+                */
+               if (*q == '/') {
+                       q++;
+                       goto redo;
+               }
+
+               /*
+                *      Get rid of /./././
+                */
+               if ((q[0] == '.') &&
+                   (q[1] == '/')) {
+                       q += 2;
+                       goto redo;
+               }
+
+               /*
+                *      Get rid of /../../../
+                */
+               if ((q[0] == '.') && (q[1] == '.') &&
+                   (q[2] == '/')) {
+                       q += 3;
+                       goto redo;
+               }
+       }
+       *p = '\0';
+
+       return (p - out);
+}
+
 /** Escapes the raw string such that it should be safe to use as part of a file path
  *
  * This function is designed to produce a string that's still readable but portable
@@ -311,7 +391,7 @@ size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, ch
                /*
                 *      Encode multibyte UTF8 chars
                 */
-               utf8_len = fr_utf8_char((uint8_t const *) in);
+               utf8_len = fr_utf8_char((uint8_t const *) in, -1);
                if (utf8_len > 1) {
                        if (freespace <= (utf8_len * 3)) break;
 
@@ -342,14 +422,13 @@ size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, ch
                if (((*in >= 'A') && (*in <= 'Z')) ||
                    ((*in >= 'a') && (*in <= 'z')) ||
                    ((*in >= '0') && (*in <= '9')) ||
-                   (*in == '_') || (*in == '.')) {
+                   (*in == '_')) {
                        if (freespace <= 1) break;
 
                        *out++ = *in++;
                        freespace--;
                        continue;
                }
-
                if (freespace <= 2) break;
 
                /*
@@ -397,7 +476,7 @@ ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t i
                if (((*p >= 'A') && (*p <= 'Z')) ||
                    ((*p >= 'a') && (*p <= 'z')) ||
                    ((*p >= '0') && (*p <= '9')) ||
-                   (*p == '_') || (*p == '.')) {
+                   (*p == '_')) {
                        *out++ = *p;
                        freespace--;
                        continue;
@@ -490,13 +569,7 @@ static int _request_free(REQUEST *request)
        rad_assert(!request->ev);
 
 #ifdef WITH_COA
-       if (request->coa) {
-               request->coa->parent = NULL;
-       }
-
-       if (request->parent && (request->parent->coa == request)) {
-               request->parent->coa = NULL;
-       }
+       rad_assert(request->coa == NULL);
 #endif
 
 #ifndef NDEBUG
@@ -530,11 +603,11 @@ REQUEST *request_alloc(TALLOC_CTX *ctx)
 #ifdef WITH_PROXY
        request->proxy_reply = NULL;
 #endif
-       request->config_items = NULL;
+       request->config = NULL;
        request->username = NULL;
        request->password = NULL;
        request->timestamp = time(NULL);
-       request->log.lvl = debug_flag; /* Default to global debug level */
+       request->log.lvl = rad_debug_lvl; /* Default to global debug level */
 
        request->module = "";
        request->component = "<core>";
@@ -647,7 +720,7 @@ REQUEST *request_alloc_coa(REQUEST *request)
        request->coa = request_alloc_fake(request);
        if (!request->coa) return NULL;
 
-       request->coa->options = 1;      /* is a CoA packet */
+       request->coa->options = RAD_REQUEST_OPTION_COA; /* is a CoA packet */
        request->coa->packet->code = 0; /* unknown, as of yet */
        request->coa->child_state = REQUEST_RUNNING;
        request->coa->proxy = rad_alloc(request->coa, false);
@@ -831,7 +904,7 @@ uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval
  */
 
 int rad_expand_xlat(REQUEST *request, char const *cmd,
-                   int max_argc, char *argv[], bool can_fail,
+                   int max_argc, char const *argv[], bool can_fail,
                    size_t argv_buflen, char *argv_buf)
 {
        char const *from;
@@ -1011,7 +1084,7 @@ static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_P
        if (!packet->vps) return;
 
 #ifdef WITH_VERIFY_PTR
-       fr_verify_list(file, line, packet, packet->vps);
+       fr_pair_list_verify(file, line, packet, packet->vps);
 #endif
 }
 /*
@@ -1028,8 +1101,8 @@ void verify_request(char const *file, int line, REQUEST *request)
        (void) talloc_get_type_abort(request, REQUEST);
 
 #ifdef WITH_VERIFY_PTR
-       fr_verify_list(file, line, request, request->config_items);
-       fr_verify_list(file, line, request, request->state);
+       fr_pair_list_verify(file, line, request, request->config);
+       fr_pair_list_verify(file, line, request, request->state);
 #endif
 
        if (request->packet) verify_packet(file, line, request, request->packet, "request");
@@ -1135,7 +1208,7 @@ int rad_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
                }
        }
 
-       if (ret != 0) {
+       if ((ret != 0) || !*out) {
                fr_strerror_printf("Failed resolving UID: %s", fr_syserror(ret));
                talloc_free(buff);
                errno = ret;
@@ -1200,7 +1273,7 @@ int rad_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
                }
        }
 
-       if (ret != 0) {
+       if ((ret != 0) || !*out) {
                fr_strerror_printf("Failed resolving UID: %s", fr_syserror(ret));
                talloc_free(buff);
                errno = ret;
@@ -1265,7 +1338,7 @@ int rad_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
                }
        }
 
-       if (ret != 0) {
+       if ((ret != 0) || !*out) {
                fr_strerror_printf("Failed resolving GID: %s", fr_syserror(ret));
                talloc_free(buff);
                errno = ret;
@@ -1330,7 +1403,7 @@ int rad_getgrnam(TALLOC_CTX *ctx, struct group **out, char const *name)
                }
        }
 
-       if (ret != 0) {
+       if ((ret != 0) || !*out) {
                fr_strerror_printf("Failed resolving GID: %s", fr_syserror(ret));
                talloc_free(buff);
                errno = ret;
@@ -1423,7 +1496,6 @@ int rad_prints_gid(TALLOC_CTX *ctx, char *out, size_t outlen, gid_t gid)
 static bool doing_setuid = false;
 static uid_t suid_down_uid = (uid_t)-1;
 
-#  if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
 /** Set the uid and gid used when dropping privileges
  *
  * @note if this function hasn't been called, rad_suid_down will have no effect.
@@ -1436,6 +1508,7 @@ void rad_suid_set_down_uid(uid_t uid)
        doing_setuid = true;
 }
 
+#  if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
 void rad_suid_up(void)
 {
        uid_t ruid, euid, suid;
@@ -1500,26 +1573,32 @@ void rad_suid_down_permanent(void)
        fr_reset_dumpable();
 }
 #  else
-void rad_suid_set_down_uid(UNUSED uid_t uid)
-{
-}
 /*
  *     Much less secure...
  */
 void rad_suid_up(void)
 {
+       if (!doing_setuid) return;
+
+       if (seteuid(0) < 0) {
+               ERROR("Failed switching up to euid 0: %s", fr_syserror(errno));
+               fr_exit_now(1);
+       }
+
 }
 
 void rad_suid_down(void)
 {
        if (!doing_setuid) return;
 
-       if (setuid(suid_down_uid) < 0) {
+       if (geteuid() == suid_down_uid) return;
+
+       if (seteuid(suid_down_uid) < 0) {
                struct passwd *passwd;
                char const *name;
 
                name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
-               ERROR("Failed switching to uid %s: %s", name, fr_syserror(errno));
+               ERROR("Failed switching to euid %s: %s", name, fr_syserror(errno));
                talloc_free(passwd);
                fr_exit_now(1);
        }
@@ -1529,11 +1608,36 @@ void rad_suid_down(void)
 
 void rad_suid_down_permanent(void)
 {
+       if (!doing_setuid) return;
+
+       /*
+        *      Already done.  Don't do anything else.
+        */
+       if (getuid() == suid_down_uid) return;
+
+       /*
+        *      We're root, but running as a normal user.  Fix that,
+        *      so we can call setuid().
+        */
+       if (geteuid() == suid_down_uid) {
+               rad_suid_up();
+       }
+
+       if (setuid(suid_down_uid) < 0) {
+               struct passwd *passwd;
+               char const *name;
+
+               name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
+               ERROR("Failed switching permanently to uid %s: %s", name, fr_syserror(errno));
+               talloc_free(passwd);
+               fr_exit_now(1);
+       }
+
        fr_reset_dumpable();
 }
 #  endif /* HAVE_SETRESUID && HAVE_GETRESUID */
 #else  /* HAVE_SETUID */
-void rad_suid_set_down_uid(UNUSED uid_t uid)
+void rad_suid_set_down_uid(uid_t uid)
 {
 }
 void rad_suid_up(void)
index 21f06f1..4a4a156 100644 (file)
@@ -71,7 +71,7 @@ int ssl_check_consistency(void)
         *      1.0.0 and only allow moving backwards within a patch
         *      series.
         */
-       if (ssl_built & 0xf00000000) {
+       if (ssl_built & 0xf0000000) {
                if ((ssl_built & 0xfffff000) != (ssl_linked & 0xfffff000) ||
                    (ssl_built & 0x00000ff0) > (ssl_linked & 0x00000ff0)) goto mismatch;
        /*
@@ -552,10 +552,10 @@ void version_print(void)
 
                talloc_free(versions);
 
-               DEBUG3("Endianess:");
-#if defined(RADIUS_LITTLE_ENDIAN)
+               DEBUG3("Endianness:");
+#if defined(FR_LITTLE_ENDIAN)
                DEBUG3("  little");
-#elif defined(RADIUS_BIG_ENDIAN)
+#elif defined(FR_BIG_ENDIAN)
                DEBUG3("  big");
 #else
                DEBUG3("  unknown");
index 8341562..87c206f 100644 (file)
@@ -37,8 +37,8 @@ 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.
+       xlat_func_t             func;                   //!< xlat function.
+       xlat_escape_t   escape;                 //!< Escape function to apply to dynamic input to func.
        bool                    internal;               //!< If true, cannot be redefined.
 } xlat_t;
 
@@ -64,7 +64,7 @@ struct xlat_exp {
        xlat_exp_t *child;      //!< Nested expansion.
        xlat_exp_t *alternate;  //!< Alternative expansion if this one expanded to a zero length string.
 
-       value_pair_tmpl_t attr; //!< An attribute template.
+       vp_tmpl_t attr; //!< An attribute template.
        xlat_t const *xlat;     //!< The xlat expansion to expand format with.
 };
 
@@ -167,10 +167,14 @@ static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
                return snprintf(out, outlen, "%u", htonl((*(uint32_t *)(vp->vp_ipv4prefix + 2))));
 
        case PW_TYPE_INTEGER:
-       case PW_TYPE_DATE:
                return snprintf(out, outlen, "%u", vp->vp_integer);
+
+       case PW_TYPE_DATE:
+               return snprintf(out, outlen, "%u", vp->vp_date);
+
        case PW_TYPE_BYTE:
                return snprintf(out, outlen, "%u", (unsigned int) vp->vp_byte);
+
        case PW_TYPE_SHORT:
                return snprintf(out, outlen, "%u", (unsigned int) vp->vp_short);
 
@@ -383,7 +387,7 @@ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char con
        VALUE_PAIR *vp;
        vp_cursor_t cursor;
 
-       value_pair_tmpl_t vpt;
+       vp_tmpl_t vpt;
 
        if (!RDEBUG_ENABLED2) {
                *out = '\0';
@@ -470,7 +474,7 @@ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char con
                                              &vp->data, vp->vp_length);
                        if (ret < 0) goto next_type;    /* We expect some to fail */
 
-                       value = vp_data_aprints_value(dst, type->number, NULL, dst, (size_t)ret, '\'');
+                       value = value_data_aprints(dst, type->number, NULL, dst, (size_t)ret, '\'');
                        if (!value) goto next_type;
 
                        if ((pad = (11 - strlen(type->name))) < 0) {
@@ -491,6 +495,35 @@ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char con
        return 0;
 }
 
+/** Processes fmt as a map string and applies it to the current request
+ *
+ * e.g. "%{map:&User-Name := 'foo'}"
+ *
+ * Allows sets of modifications to be cached and then applied.
+ * Useful for processing generic attributes from LDAP.
+ */
+static ssize_t xlat_map(UNUSED void *instance, REQUEST *request,
+                       char const *fmt, char *out, size_t outlen)
+{
+       vp_map_t *map = NULL;
+       int ret;
+
+       if (map_afrom_attr_str(request, &map, fmt,
+                              REQUEST_CURRENT, PAIR_LIST_REQUEST,
+                              REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+               REDEBUG("Failed parsing \"%s\" as map: %s", fmt, fr_strerror());
+               return -1;
+       }
+
+       RINDENT();
+       ret = map_to_request(request, map, map_to_vp, NULL);
+       REXDENT();
+       talloc_free(map);
+       if (ret < 0) return strlcpy(out, "0", outlen);
+
+       return strlcpy(out, "1", outlen);
+}
+
 /** Prints the current module processing the request
  *
  */
@@ -635,7 +668,7 @@ static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
        /*
         *  Expand to previous (or current) level
         */
-       snprintf(out, outlen, "%d", request->log.lvl & RAD_REQUEST_OPTION_DEBUG4);
+       snprintf(out, outlen, "%d", request->log.lvl);
 
        /*
         *  Assume we just want to get the current value and NOT set it to 0
@@ -645,7 +678,7 @@ static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
 
        level = atoi(fmt);
        if (level == 0) {
-               request->log.lvl = RAD_REQUEST_OPTION_NONE;
+               request->log.lvl = RAD_REQUEST_LVL_NONE;
                request->log.func = NULL;
        } else {
                if (level > 4) level = 4;
@@ -696,7 +729,7 @@ static xlat_t *xlat_find(char const *name)
  * @param[in] instance of module that's registering the xlat function.
  * @return 0 on success, -1 on failure
  */
-int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance)
+int xlat_register(char const *name, xlat_func_t func, xlat_escape_t escape, void *instance)
 {
        xlat_t  *c;
        xlat_t  my_xlat;
@@ -750,6 +783,7 @@ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING esc
                XLAT_REGISTER(attr_num);
                XLAT_REGISTER(string);
                XLAT_REGISTER(xlat);
+               XLAT_REGISTER(map);
                XLAT_REGISTER(module);
                XLAT_REGISTER(debug_attr);
 #if defined(HAVE_REGEX) && defined(HAVE_PCRE)
@@ -818,7 +852,7 @@ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING esc
  * @param[in] func unused.
  * @param[in] instance data.
  */
-void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance)
+void xlat_unregister(char const *name, UNUSED xlat_func_t func, void *instance)
 {
        xlat_t  *c;
        xlat_t          my_xlat;
@@ -1099,7 +1133,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
 static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
                                     bool brace, char const **error);
 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
-                          RADIUS_ESCAPE_STRING escape, void *escape_ctx);
+                          xlat_escape_t escape, void *escape_ctx);
 
 static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
                                         char const **error)
@@ -1246,6 +1280,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
         *      Check for empty expressions %{}
         */
        if ((*q == '}') && (q == p)) {
+               talloc_free(node);
                *error = "Empty expression is invalid";
                return -(p - fmt);
        }
@@ -1268,7 +1303,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                        XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p);
 
                        slen = xlat_tokenize_literal(node, p, &node->child, true, error);
-                       if (slen <= 0) {
+                       if (slen < 0) {
                                talloc_free(node);
                                return slen - (p - fmt);
                        }
@@ -1300,6 +1335,8 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                } else {
                        *error = fr_strerror();
                }
+
+               talloc_free(node);
                return slen - (p - fmt);
        }
 
@@ -1308,6 +1345,12 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
         */
        if (node->attr.type == TMPL_TYPE_ATTR_UNDEFINED) {
                node->xlat = xlat_find(node->attr.tmpl_unknown_name);
+               if (node->xlat && node->xlat->instance && !node->xlat->internal) {
+                       talloc_free(node);
+                       *error = "Missing content in expansion";
+                       return -(p - fmt) - slen;
+               }
+
                if (node->xlat) {
                        node->type = XLAT_VIRTUAL;
                        node->fmt = node->attr.tmpl_unknown_name;
@@ -1332,7 +1375,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                *error = "No matching closing brace";
                return -1;      /* second character of format string */
        }
-       p++;
+       *p++ = '\0';
        *head = node;
        rad_assert(node->next == NULL);
 
@@ -1364,7 +1407,9 @@ static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **he
                                *error = "Invalid escape at end of string";
                                return -(p - fmt);
                        }
+
                        p += 2;
+                       node->len += 2;
                        continue;
                }
 
@@ -1418,27 +1463,31 @@ 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("%dlmntDGHISTYv", p[1])) {
-                                       talloc_free(node);
-                                       *error = "Invalid variable expansion";
-                                       p++;
-                                       return - (p - fmt);
+                       if (!p[1] || !strchr("%}dlmntDGHISTYv", p[1])) {
+                               talloc_free(node);
+                               *error = "Invalid variable expansion";
+                               p++;
+                               return - (p - fmt);
                        }
 
                        next = talloc_zero(node, xlat_exp_t);
                        next->len = 1;
 
-                       if (p[1] == '%') {
-                               next->fmt = talloc_typed_strdup(next, "%");
+                       switch (p[1]) {
+                       case '%':
+                       case '}':
+                               next->fmt = talloc_strndup(next, p + 1, 1);
 
-                               XLAT_DEBUG("LITERAL-PERCENT <-- %s", next->fmt);
+                               XLAT_DEBUG("LITERAL-ESCAPED <-- %s", next->fmt);
                                next->type = XLAT_LITERAL;
+                               break;
 
-                       } else {
+                       default:
                                next->fmt = p + 1;
 
                                XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
                                next->type = XLAT_PERCENT;
+                               break;
                        }
 
                        node->next = next;
@@ -1757,7 +1806,7 @@ static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp
                return slen;
        }
 
-       if (*head && (debug_flag > 2)) {
+       if (*head && (rad_debug_lvl > 2)) {
                DEBUG("%s", fmt);
                DEBUG("Parsed xlat tree:");
                xlat_tokenize_debug(*head, 0);
@@ -1774,18 +1823,26 @@ static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp
 }
 
 
-static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, value_pair_tmpl_t const *vpt,
+static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, vp_tmpl_t const *vpt,
                        bool escape, bool return_null)
 {
        VALUE_PAIR *vp = NULL, *virtual = NULL;
        RADIUS_PACKET *packet = NULL;
        DICT_VALUE *dv;
        char *ret = NULL;
-       int err;
 
+       vp_cursor_t cursor;
        char quote = escape ? '"' : '\0';
 
-       vp_cursor_t cursor;
+       rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST));
+
+       /*
+        *      We only support count and concatenate operations on lists.
+        */
+       if (vpt->type == TMPL_TYPE_LIST) {
+               vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
+               goto do_print;
+       }
 
        /*
         *      See if we're dealing with an attribute in the request
@@ -1793,7 +1850,7 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, value_pair_tmpl_t con
         *      This allows users to manipulate virtual attributes as if
         *      they were real ones.
         */
-       vp = tmpl_cursor_init(&err, &cursor, request, vpt);
+       vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
        if (vp) goto do_print;
 
        /*
@@ -1887,15 +1944,15 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, value_pair_tmpl_t con
         *      various VP functions.
         */
        case PW_PACKET_AUTHENTICATION_VECTOR:
-               virtual = pairalloc(ctx, vpt->tmpl_da);
-               pairmemcpy(virtual, packet->vector, sizeof(packet->vector));
+               virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
+               fr_pair_value_memcpy(virtual, packet->vector, sizeof(packet->vector));
                vp = virtual;
                break;
 
        case PW_CLIENT_IP_ADDRESS:
        case PW_PACKET_SRC_IP_ADDRESS:
                if (packet->src_ipaddr.af == AF_INET) {
-                       virtual = pairalloc(ctx, vpt->tmpl_da);
+                       virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
                        virtual->vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
                        vp = virtual;
                }
@@ -1903,7 +1960,7 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, value_pair_tmpl_t con
 
        case PW_PACKET_DST_IP_ADDRESS:
                if (packet->dst_ipaddr.af == AF_INET) {
-                       virtual = pairalloc(ctx, vpt->tmpl_da);
+                       virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
                        virtual->vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
                        vp = virtual;
                }
@@ -1911,7 +1968,7 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, value_pair_tmpl_t con
 
        case PW_PACKET_SRC_IPV6_ADDRESS:
                if (packet->src_ipaddr.af == AF_INET6) {
-                       virtual = pairalloc(ctx, vpt->tmpl_da);
+                       virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
                        memcpy(&virtual->vp_ipv6addr,
                               &packet->src_ipaddr.ipaddr.ip6addr,
                               sizeof(packet->src_ipaddr.ipaddr.ip6addr));
@@ -1921,7 +1978,7 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, value_pair_tmpl_t con
 
        case PW_PACKET_DST_IPV6_ADDRESS:
                if (packet->dst_ipaddr.af == AF_INET6) {
-                       virtual = pairalloc(ctx, vpt->tmpl_da);
+                       virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
                        memcpy(&virtual->vp_ipv6addr,
                               &packet->dst_ipaddr.ipaddr.ip6addr,
                               sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
@@ -1930,13 +1987,13 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, value_pair_tmpl_t con
                break;
 
        case PW_PACKET_SRC_PORT:
-               virtual = pairalloc(ctx, vpt->tmpl_da);
+               virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
                virtual->vp_integer = packet->src_port;
                vp = virtual;
                break;
 
        case PW_PACKET_DST_PORT:
-               virtual = pairalloc(ctx, vpt->tmpl_da);
+               virtual = fr_pair_afrom_da(ctx, vpt->tmpl_da);
                virtual->vp_integer = packet->dst_port;
                vp = virtual;
                break;
@@ -1984,8 +2041,9 @@ do_print:
        {
                int count = 0;
 
-               fr_cursor_first(&cursor);
-               while (fr_cursor_next_by_da(&cursor, vpt->tmpl_da, vpt->tmpl_tag)) count++;
+               for (vp = tmpl_cursor_init(NULL, &cursor, request, vpt);
+                    vp;
+                    vp = tmpl_cursor_next(&cursor, vpt)) count++;
 
                return talloc_typed_asprintf(ctx, "%d", count);
        }
@@ -2040,13 +2098,13 @@ 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)
+                        xlat_escape_t escape, void *escape_ctx, int lvl)
 {
        ssize_t rcode;
        char *str = NULL, *child;
        char const *p;
 
-       XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type);
+       XLAT_DEBUG("%.*sxlat aprint %d %s", lvl, xlat_spaces, node->type, node->fmt);
 
        switch (node->type) {
                /*
@@ -2187,17 +2245,24 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
 
        case XLAT_MODULE:
                XLAT_DEBUG("xlat_aprint MODULE");
-               if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
-                       return NULL;
+
+               if (node->child) {
+                       if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) {
+                               return NULL;
+                       }
+
+                       XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
+               } else {
+                       XLAT_DEBUG("%.*sEXPAND mod %s", lvl, xlat_spaces, node->fmt);
+                       child = talloc_typed_strdup(ctx, "");
                }
 
-               XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
                XLAT_DEBUG("%.*s      ---> %s", lvl, xlat_spaces, child);
 
                /*
                 *      Smash \n --> CR.
                 *
-                *      The OUTPUT of xlat is a printable string.  The INPUT might not be...
+                *      The OUTPUT of xlat is a "raw" string.  The INPUT is a printable string.
                 *
                 *      This is really the reverse of fr_prints().
                 */
@@ -2268,14 +2333,26 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                rad_assert(node->alternate != NULL);
 
                str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl);
-               if (str) break;
+               if (str) {
+                       XLAT_DEBUG("ALTERNATE got string: %s", str);
+                       break;
+               }
 
+               XLAT_DEBUG("ALTERNATE going to alternate");
                str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl);
                break;
 
        }
 
        /*
+        *      If there's no data, return that, instead of an empty string.
+        */
+       if (str && !str[0]) {
+               talloc_free(str);
+               return NULL;
+       }
+
+       /*
         *      Escape the non-literals we found above.
         */
        if (str && escape) {
@@ -2292,7 +2369,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
 
 
 static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * const head,
-                          RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+                          xlat_escape_t escape, void *escape_ctx)
 {
        int i, list;
        size_t total;
@@ -2375,7 +2452,7 @@ static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * cons
 
 /** Replace %whatever in a string.
  *
- * See 'doc/variables.txt' for more information.
+ * See 'doc/configuration/variables.rst' for more information.
  *
  * @param[out] out Where to write pointer to output buffer.
  * @param[in] outlen Size of out.
@@ -2386,7 +2463,7 @@ static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * cons
  * @return length of string written @bug should really have -1 for failure
  */
 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)
+                                 xlat_escape_t escape, void *escape_ctx)
 {
        char *buff;
        ssize_t len;
@@ -2401,6 +2478,7 @@ static ssize_t xlat_expand_struct(char **out, size_t outlen, REQUEST *request, x
        }
 
        len = strlen(buff);
+
        /*
         *      If out doesn't point to an existing buffer
         *      copy the pointer to our buffer over.
@@ -2419,11 +2497,11 @@ static ssize_t xlat_expand_struct(char **out, size_t outlen, REQUEST *request, x
 }
 
 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));
+                          xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull (1, 3, 4));
 
 /** Replace %whatever in a string.
  *
- * See 'doc/variables.txt' for more information.
+ * See 'doc/configuration/variables.rst' for more information.
  *
  * @param[out] out Where to write pointer to output buffer.
  * @param[in] outlen Size of out.
@@ -2434,7 +2512,7 @@ static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char con
  * @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)
+                          xlat_escape_t escape, void *escape_ctx)
 {
        ssize_t len;
        xlat_exp_t *node;
@@ -2468,15 +2546,15 @@ static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char con
 
 /** Try to convert an xlat to a tmpl for efficiency
  *
- * @param ctx to allocate new value_pair_tmpl_t in.
+ * @param ctx to allocate new vp_tmpl_t in.
  * @param node to convert.
- * @return NULL if unable to convert (not necessarily error), or a new value_pair_tmpl_t.
+ * @return NULL if unable to convert (not necessarily error), or a new vp_tmpl_t.
  */
-value_pair_tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
+vp_tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
 {
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
-       if (node->next || (node->type != XLAT_ATTRIBUTE)) return NULL;
+       if (node->next || (node->type != XLAT_ATTRIBUTE) || (node->attr.type != TMPL_TYPE_ATTR)) return NULL;
 
        /*
         *   Concat means something completely different as an attribute reference
@@ -2497,38 +2575,39 @@ value_pair_tmpl_t *xlat_to_tmpl_attr(TALLOC_CTX *ctx, xlat_exp_t *node)
  *
  * @param ctx to allocate new xlat_expt_t in.
  * @param vpt to convert.
- * @return NULL if unable to convert (not necessarily error), or a new value_pair_tmpl_t.
+ * @return NULL if unable to convert (not necessarily error), or a new vp_tmpl_t.
  */
-xlat_exp_t *xlat_from_tmpl_attr(TALLOC_CTX *ctx, value_pair_tmpl_t *vpt)
+xlat_exp_t *xlat_from_tmpl_attr(TALLOC_CTX *ctx, vp_tmpl_t *vpt)
 {
        xlat_exp_t *node;
 
        if (vpt->type != TMPL_TYPE_ATTR) return NULL;
 
        node = talloc_zero(ctx, xlat_exp_t);
-       node->fmt = talloc_memdup(node, vpt->name, vpt->len);
+       node->type = XLAT_ATTRIBUTE;
+       node->fmt = talloc_bstrndup(node, vpt->name, vpt->len);
        tmpl_init(&node->attr, TMPL_TYPE_ATTR, node->fmt, talloc_array_length(node->fmt) - 1);
        memcpy(&node->attr.data, &vpt->data, sizeof(vpt->data));
 
        return node;
 }
 
-ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
+ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, void *ctx)
 {
        return xlat_expand(&out, outlen, request, fmt, escape, ctx);
 }
 
-ssize_t radius_xlat_struct(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat, RADIUS_ESCAPE_STRING escape, void *ctx)
+ssize_t radius_xlat_struct(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx)
 {
        return xlat_expand_struct(&out, outlen, request, xlat, escape, ctx);
 }
 
-ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
+ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, xlat_escape_t escape, void *ctx)
 {
        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)
+ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, xlat_escape_t escape, void *ctx)
 {
        return xlat_expand_struct(out, 0, request, xlat, escape, ctx);
 }
index 7af93fa..d098a8a 100644 (file)
@@ -1 +1 @@
-SUBMAKEFILES := proto_dhcp.mk rlm_dhcp.mk dhcpclient.mk
+SUBMAKEFILES := libfreeradius-dhcp.mk proto_dhcp.mk rlm_dhcp.mk dhcpclient.mk
index 774ebeb..6b47dcf 100644 (file)
@@ -5,8 +5,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -55,8 +54,8 @@ RCSID("$Id$")
 #endif
 
 /* @todo: this is a hack */
-#  define DEBUG                        if (fr_debug_flag && fr_log_fp) fr_printf_log
-#  define debug_pair(vp)       do { if (fr_debug_flag && fr_log_fp) { \
+#  define DEBUG                        if (fr_debug_lvl && fr_log_fp) fr_printf_log
+#  define debug_pair(vp)       do { if (fr_debug_lvl && fr_log_fp) { \
                                        vp_print(fr_log_fp, vp); \
                                     } \
                                } while(0)
@@ -73,7 +72,7 @@ static uint8_t eth_bcast[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 /* Discard raw packets which we are not interested in. Allow to trace why we discard. */
 #define DISCARD_RP(...) { \
-       if (fr_debug_flag > 2) { \
+       if (fr_debug_lvl > 2) { \
                fprintf(stdout, "dhcpclient: discarding received packet: "); \
                fprintf(stdout, ## __VA_ARGS__); \
                fprintf(stdout, "\n"); \
@@ -143,8 +142,16 @@ static char const *dhcp_message_types[] = {
        "DHCP-Release",
        "DHCP-Inform",
        "DHCP-Force-Renew",
+       "DHCP-Lease-Query",
+       "DHCP-Lease-Unassigned",
+       "DHCP-Lease-Unknown",
+       "DHCP-Lease-Active",
+       "DHCP-Bulk-Lease-Query",
+       "DHCP-Lease-Query-Done"
 };
 
+#define DHCP_MAX_MESSAGE_TYPE (sizeof(dhcp_message_types) / sizeof(dhcp_message_types[0]))
+
 static int dhcp_header_sizes[] = {
        1, 1, 1, 1,
        4, 2, 2, 4,
@@ -295,14 +302,14 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
                return NULL;
        }
 
-       if (packet->data[1] != 1) {
+       if (packet->data[1] > 1) {
                fr_strerror_printf("DHCP can only receive ethernet requests, not type %02x",
                      packet->data[1]);
                rad_free(&packet);
                return NULL;
        }
 
-       if (packet->data[2] != 6) {
+       if ((packet->data[2] != 0) && (packet->data[2] != 6)) {
                fr_strerror_printf("Ethernet HW length is wrong length %d",
                        packet->data[2]);
                rad_free(&packet);
@@ -331,8 +338,8 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
                return NULL;
        }
 
-       if ((code[1] < 1) || (code[2] == 0) || (code[2] > 8)) {
-               fr_strerror_printf("Unknown value for message-type option");
+       if ((code[1] < 1) || (code[2] == 0) || (code[2] >= DHCP_MAX_MESSAGE_TYPE)) {
+               fr_strerror_printf("Unknown value %d for message-type option", code[2]);
                rad_free(&packet);
                return NULL;
        }
@@ -386,13 +393,13 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
        fr_sockaddr2ipaddr(&src, sizeof_src, &packet->src_ipaddr, &port);
        packet->src_port = port;
 
-       if (fr_debug_flag > 1) {
+       if (fr_debug_lvl > 1) {
                char type_buf[64];
                char const *name = type_buf;
                char src_ip_buf[256], dst_ip_buf[256];
 
                if ((packet->code >= PW_DHCP_DISCOVER) &&
-                   (packet->code <= PW_DHCP_INFORM)) {
+                   (packet->code < (1024 + DHCP_MAX_MESSAGE_TYPE))) {
                        name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
                } else {
                        snprintf(type_buf, sizeof(type_buf), "%d",
@@ -438,7 +445,7 @@ int fr_dhcp_send(RADIUS_PACKET *packet)
                return -1;
        }
 
-       if (fr_debug_flag > 1) {
+       if (fr_debug_lvl > 1) {
                char type_buf[64];
                char const *name = type_buf;
 #ifdef WITH_UDPFROMTO
@@ -447,7 +454,7 @@ int fr_dhcp_send(RADIUS_PACKET *packet)
                char dst_ip_buf[INET6_ADDRSTRLEN];
 
                if ((packet->code >= PW_DHCP_DISCOVER) &&
-                   (packet->code <= PW_DHCP_INFORM)) {
+                   (packet->code < (1024 + DHCP_MAX_MESSAGE_TYPE))) {
                        name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
                } else {
                        snprintf(type_buf, sizeof(type_buf), "%d",
@@ -546,123 +553,12 @@ static int fr_dhcp_array_members(size_t *len, DICT_ATTR const *da)
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  @endverbatim
  *
- * So although the vendor is identified, the format of the data isn't specified
- * so we can't actually resolve the suboption to an attribute.
- *
- * To get around that, we create an attribute with a vendor matching the
- * enterprise number, and attr 0.
- *
- * How the suboption data is then processed, is dependent on what type
- * \<iana\>.0 is defined as in the dictionary.
- *
- * @param[in,out] tlv to decode. *tlv will be set to the head of the list of suboptions and original will be freed.
- * @param[in] ctx context to alloc new attributes in.
- * @param[in] data to parse.
- * @param[in] len length of data to parse.
+ * So although the vendor is identified, the format of the data isn't
+ * specified so we can't actually resolve the suboption to an
+ * attribute.  For now, we just convert it to an attribute of
+ * DHCP-Vendor-Specific-Information with raw octets contents.
  */
-static int fr_dhcp_decode_vsa(TALLOC_CTX *ctx, VALUE_PAIR **tlv, uint8_t const *data, size_t len)
-{
-       uint8_t const *p, *q;
-       vp_cursor_t cursor;
-
-       VALUE_PAIR *head;
-
-       if (len < 4) goto malformed;
-
-       p = data;
-       q = p + len;
-       while (p < q) {
-               if (p + 5 >= q) goto malformed;
-               p += sizeof(uint32_t);
-               p += p[0];
 
-               /*
-                *      Check if length > the length of the buffer we have left
-                */
-               if (p >= q) goto malformed;
-               p++;
-       }
-
-       head = NULL;
-       fr_cursor_init(&cursor, &head);
-
-       /*
-        *      Now we know its sane, start decoding!
-        */
-       p = data;
-       while (p < q) {
-               uint32_t vendor;
-               DICT_ATTR const *da;
-               VALUE_PAIR *vp;
-
-               vendor = ntohl(*((uint32_t const *) p));
-               /*
-                *      This is pretty much all we can do.  RFC 4243 doesn't specify
-                *      an attribute field, so it's up to vendors to figure out how
-                *      they want to encode their attributes.
-                */
-               da = dict_attrbyvalue(0, vendor);
-               if (!da) {
-                       da = dict_unknown_afrom_fields(ctx, 0, vendor);
-                       if (!da) {
-                               pairfree(&head);
-                               goto malformed;
-                       }
-               }
-               vp = pairalloc(ctx, da);
-               if (!vp) {
-                       pairfree(&head);
-                       return -1;
-               }
-               vp->op = T_OP_ADD;
-               pairsteal(ctx, vp); /* for unknown attributes hack */
-
-               if (fr_dhcp_attr2vp(ctx, &vp, p + 5, p[4]) < 0) {
-                       dict_attr_free(&da);
-                       pairfree(&head);
-                       return -1;
-               }
-
-               fr_cursor_merge(&cursor, vp);
-               dict_attr_free(&da); /* for unknown attributes hack */
-
-               p += 4 + 1 + p[4];      /* vendor id (4) + len (1) + vsa len (n) */
-       }
-
-       /*
-        *      The caller allocated TLV, if decoding it generated additional
-        *      attributes, we now need to free it, and write the HEAD of our
-        *      new list of attributes in its place.
-        */
-       if (head) {
-               vp_cursor_t tlv_cursor;
-
-               /*
-                *      Free the old TLV attribute
-                */
-               TALLOC_FREE(*tlv);
-
-               /*
-                *      Cursor not necessary but means we don't have to set
-                *      ->next directly.
-                */
-               fr_cursor_init(&tlv_cursor, tlv);
-               fr_cursor_merge(&tlv_cursor, head);
-       }
-
-       return 0;
-
-malformed:
-       (*tlv)->vp_tlv = talloc_array(*tlv, uint8_t, len);
-       if (!(*tlv)->vp_tlv) {
-               fr_strerror_printf("No memory");
-               return -1;
-       }
-       memcpy((*tlv)->vp_tlv, data, len);
-       (*tlv)->vp_length = len;
-
-       return 0;
-}
 
 /** Decode DHCP suboptions
  *
@@ -757,7 +653,7 @@ static int fr_dhcp_decode_suboption(TALLOC_CTX *ctx, VALUE_PAIR **tlv, uint8_t c
                if (!da) {
                        da = dict_unknown_afrom_fields(ctx, attr, (*tlv)->da->vendor);
                        if (!da) {
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
                                return -1;
                        }
                }
@@ -766,17 +662,17 @@ static int fr_dhcp_decode_suboption(TALLOC_CTX *ctx, VALUE_PAIR **tlv, uint8_t c
                a_p = p + 2;
                num_entries = fr_dhcp_array_members(&a_len, da);
                for (i = 0; i < num_entries; i++) {
-                       vp = pairalloc(ctx, da);
+                       vp = fr_pair_afrom_da(ctx, da);
                        if (!vp) {
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
                                return -1;
                        }
-                       vp->op = T_OP_ADD;
-                       pairsteal(ctx, vp); /* for unknown attributes hack */
+                       vp->op = T_OP_EQ;
+                       fr_pair_steal(ctx, vp); /* for unknown attributes hack */
 
                        if (fr_dhcp_attr2vp(ctx, &vp, a_p, a_len) < 0) {
                                dict_attr_free(&da);
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
                                goto malformed;
                        }
                        fr_cursor_merge(&cursor, vp);
@@ -813,13 +709,8 @@ static int fr_dhcp_decode_suboption(TALLOC_CTX *ctx, VALUE_PAIR **tlv, uint8_t c
        return 0;
 
 malformed:
-       (*tlv)->vp_tlv = talloc_array(*tlv, uint8_t, len);
-       if (!(*tlv)->vp_tlv) {
-               fr_strerror_printf("No memory");
-               return -1;
-       }
-       memcpy((*tlv)->vp_tlv, data, len);
-       (*tlv)->vp_length = len;
+       fr_pair_to_unknown(*tlv);
+       fr_pair_value_memcpy(*tlv, data, len);
 
        return 0;
 }
@@ -873,7 +764,7 @@ static int fr_dhcp_attr2vp(TALLOC_CTX *ctx, VALUE_PAIR **vp_p, uint8_t const *da
                q = end = data + len;
 
                if (!vp->da->flags.array) {
-                       pairstrncpy(vp, (char const *)p, q - p);
+                       fr_pair_value_bstrncpy(vp, (char const *)p, q - p);
                        break;
                }
 
@@ -887,14 +778,14 @@ static int fr_dhcp_attr2vp(TALLOC_CTX *ctx, VALUE_PAIR **vp_p, uint8_t const *da
                        /* Malformed but recoverable */
                        if (!q) q = end;
 
-                       pairstrncpy(vp, (char const *)p, q - p);
+                       fr_pair_value_bstrncpy(vp, (char const *)p, q - p);
                        p = q + 1;
 
                        /* Need another VP for the next round */
                        if (p < end) {
-                               vp = pairalloc(ctx, vp->da);
+                               vp = fr_pair_afrom_da(ctx, vp->da);
                                if (!vp) {
-                                       pairfree(vp_p);
+                                       fr_pair_list_free(vp_p);
                                        return -1;
                                }
                                fr_cursor_insert(&cursor, vp);
@@ -915,11 +806,11 @@ static int fr_dhcp_attr2vp(TALLOC_CTX *ctx, VALUE_PAIR **vp_p, uint8_t const *da
         *      vp's original DICT_ATTR with an unknown one.
         */
        raw:
-               if (pair2unknown(vp) < 0) return -1;
+               if (fr_pair_to_unknown(vp) < 0) return -1;
 
        case PW_TYPE_OCTETS:
                if (len > 255) return -1;
-               pairmemcpy(vp, data, len);
+               fr_pair_value_memcpy(vp, data, len);
                break;
 
        /*
@@ -928,12 +819,6 @@ static int fr_dhcp_attr2vp(TALLOC_CTX *ctx, VALUE_PAIR **vp_p, uint8_t const *da
        case PW_TYPE_TLV:
                return fr_dhcp_decode_suboption(ctx, vp_p, data, len);
 
-       /*
-        *      For option 82.9
-        */
-       case PW_TYPE_VSA:
-               return fr_dhcp_decode_vsa(ctx, vp_p, data, len);
-
        default:
                fr_strerror_printf("Internal sanity check %d %d", vp->da->type, __LINE__);
                return -1;
@@ -994,15 +879,15 @@ ssize_t fr_dhcp_decode_options(TALLOC_CTX *ctx, VALUE_PAIR **out, uint8_t const
                if (!da) {
                        da = dict_unknown_afrom_fields(ctx, p[0], DHCP_MAGIC_VENDOR);
                        if (!da) {
-                               pairfree(out);
+                               fr_pair_list_free(out);
                                return -1;
                        }
-                       vp = pairalloc(ctx, da);
+                       vp = fr_pair_afrom_da(ctx, da);
                        if (!vp) {
-                               pairfree(out);
+                               fr_pair_list_free(out);
                                return -1;
                        }
-                       pairmemcpy(vp, a_p, a_len);
+                       fr_pair_value_memcpy(vp, a_p, a_len);
                        fr_cursor_insert(&cursor, vp);
 
                        goto next;
@@ -1014,25 +899,19 @@ ssize_t fr_dhcp_decode_options(TALLOC_CTX *ctx, VALUE_PAIR **out, uint8_t const
                 */
                num_entries = fr_dhcp_array_members(&a_len, da);
                for (i = 0; i < num_entries; i++) {
-                       vp = pairalloc(ctx, da);
+                       vp = fr_pair_afrom_da(ctx, da);
                        if (!vp) {
-                               pairfree(out);
+                               fr_pair_list_free(out);
                                return -1;
                        }
-                       vp->op = T_OP_ADD;
+                       vp->op = T_OP_EQ;
 
                        if (fr_dhcp_attr2vp(ctx, &vp, a_p, a_len) < 0) {
-                               pairfree(&vp);
-                               pairfree(out);
+                               fr_pair_list_free(&vp);
+                               fr_pair_list_free(out);
                                return -1;
                        }
                        fr_cursor_merge(&cursor, vp);
-
-                       for (vp = fr_cursor_current(&cursor);
-                            vp;
-                            vp = fr_cursor_next(&cursor)) {
-                               debug_pair(vp);
-                       }
                        a_p += a_len;
                } /* loop over array entries */
        next:
@@ -1054,7 +933,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
        fr_cursor_init(&cursor, &head);
        p = packet->data;
 
-       if ((fr_debug_flag > 2) && fr_log_fp) {
+       if ((fr_debug_lvl > 2) && fr_log_fp) {
                for (i = 0; i < packet->data_len; i++) {
                        if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, "%d: ", (int) i);
                        fprintf(fr_log_fp, "%02x ", packet->data[i]);
@@ -1063,7 +942,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                fprintf(fr_log_fp, "\n");
        }
 
-       if (packet->data[1] != 1) {
+       if (packet->data[1] > 1) {
                fr_strerror_printf("Packet is not Ethernet: %u",
                      packet->data[1]);
                return -1;
@@ -1075,25 +954,32 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
        for (i = 0; i < 14; i++) {
                char *q;
 
-               vp = pairmake(packet, NULL, dhcp_header_names[i], NULL, T_OP_EQ);
+               vp = fr_pair_make(packet, NULL, dhcp_header_names[i], NULL, T_OP_EQ);
                if (!vp) {
                        char buffer[256];
                        strlcpy(buffer, fr_strerror(), sizeof(buffer));
                        fr_strerror_printf("Cannot decode packet due to internal error: %s", buffer);
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return -1;
                }
 
                /*
-                *      If chaddr does != 6 bytes it's probably not ethernet, and we should store
+                *      If chaddr != 6 bytes it's probably not ethernet, and we should store
                 *      it as an opaque type (octets).
                 */
-               if ((i == 11) && (packet->data[1] == 1) && (packet->data[2] != sizeof(vp->vp_ether))) {
-                       DICT_ATTR const *da = dict_unknown_afrom_fields(packet, vp->da->attr, vp->da->vendor);
-                       if (!da) {
-                               return -1;
+               if (i == 11) {
+                       /*
+                        *      Skip chaddr if it doesn't exist.
+                        */
+                       if ((packet->data[1] == 0) || (packet->data[2] == 0)) continue;
+
+                       if ((packet->data[1] == 1) && (packet->data[2] != sizeof(vp->vp_ether))) {
+                               DICT_ATTR const *da = dict_unknown_afrom_fields(packet, vp->da->attr, vp->da->vendor);
+                               if (!da) {
+                                       return -1;
+                               }
+                               vp->da = da;
                        }
-                       vp->da = da;
                }
 
                switch (vp->da->type) {
@@ -1125,12 +1011,14 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                        q[dhcp_header_sizes[i]] = '\0';
                        vp->vp_length = strlen(vp->vp_strvalue);
                        if (vp->vp_length == 0) {
-                               pairfree(&vp);
+                               fr_pair_list_free(&vp);
                        }
                        break;
 
                case PW_TYPE_OCTETS:
-                       pairmemcpy(vp, p, packet->data[2]);
+                       if (packet->data[2] == 0) break;
+
+                       fr_pair_value_memcpy(vp, p, packet->data[2]);
                        break;
 
                case PW_TYPE_ETHERNET:
@@ -1140,7 +1028,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
 
                default:
                        fr_strerror_printf("BAD TYPE %d", vp->da->type);
-                       pairfree(&vp);
+                       fr_pair_list_free(&vp);
                        break;
                }
                p += dhcp_header_sizes[i];
@@ -1161,12 +1049,20 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
         */
        {
                VALUE_PAIR *options = NULL;
+               vp_cursor_t options_cursor;
 
                if (fr_dhcp_decode_options(packet, &options, packet->data + 240, packet->data_len - 240) < 0) {
                        return -1;
                }
 
-               if (options) fr_cursor_merge(&cursor, options);
+               if (options) {
+                       for (vp = fr_cursor_init(&options_cursor, &options);
+                            vp;
+                            vp = fr_cursor_next(&options_cursor)) {
+                               debug_pair(vp);
+                       }
+                       fr_cursor_merge(&cursor, options);
+               }
        }
 
        /*
@@ -1182,19 +1078,19 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                /*
                 *      DHCP Opcode is request
                 */
-               vp = pairfind(head, 256, DHCP_MAGIC_VENDOR, TAG_ANY);
+               vp = fr_pair_find_by_num(head, 256, DHCP_MAGIC_VENDOR, TAG_ANY);
                if (vp && vp->vp_integer == 3) {
                        /*
                         *      Vendor is "MSFT 98"
                         */
-                       vp = pairfind(head, 63, DHCP_MAGIC_VENDOR, TAG_ANY);
+                       vp = fr_pair_find_by_num(head, 63, DHCP_MAGIC_VENDOR, TAG_ANY);
                        if (vp && (strcmp(vp->vp_strvalue, "MSFT 98") == 0)) {
-                               vp = pairfind(head, 262, DHCP_MAGIC_VENDOR, TAG_ANY);
+                               vp = fr_pair_find_by_num(head, 262, DHCP_MAGIC_VENDOR, TAG_ANY);
 
                                /*
                                 *      Reply should be broadcast.
                                 */
-                               if (vp) vp->vp_integer |= 0x8000;
+                               if (vp) vp->vp_short |= 0x8000;
                                packet->data[10] |= 0x80;
                        }
                }
@@ -1210,8 +1106,8 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
         *      Client can request a LARGER size, but not a smaller
         *      one.  They also cannot request a size larger than MTU.
         */
-       maxms = pairfind(packet->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY);
-       mtu = pairfind(packet->vps, 26, DHCP_MAGIC_VENDOR, TAG_ANY);
+       maxms = fr_pair_find_by_num(packet->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY);
+       mtu = fr_pair_find_by_num(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");
@@ -1228,7 +1124,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                maxms->vp_integer = mtu->vp_integer;
        }
 
-       if (fr_debug_flag) fflush(stdout);
+       if (fr_debug_lvl) fflush(stdout);
 
        return 0;
 }
@@ -1267,7 +1163,7 @@ int8_t fr_dhcp_attr_cmp(void const *a, void const *b)
  * @param vp option to encode.
  * @return the length of data writen, -1 if out of buffer, -2 if unsupported type.
  */
-static ssize_t fr_dhcp_vp2attr(uint8_t *out, size_t outlen, VALUE_PAIR *vp)
+static ssize_t fr_dhcp_vp2data(uint8_t *out, size_t outlen, VALUE_PAIR *vp)
 {
        uint32_t lvalue;
        uint8_t *p = out;
@@ -1303,10 +1199,6 @@ static ssize_t fr_dhcp_vp2attr(uint8_t *out, size_t outlen, VALUE_PAIR *vp)
                memcpy(p, vp->vp_strvalue, vp->vp_length);
                break;
 
-       case PW_TYPE_TLV:       /* FIXME: split it on 255? */
-               memcpy(p, vp->vp_tlv, vp->vp_length);
-               break;
-
        case PW_TYPE_OCTETS:
                memcpy(p, vp->vp_octets, vp->vp_length);
                break;
@@ -1321,89 +1213,94 @@ static ssize_t fr_dhcp_vp2attr(uint8_t *out, size_t outlen, VALUE_PAIR *vp)
 
 /** Create a new TLV attribute from multiple sub options
  *
- * @param[in,out] ctx to allocate new attribute in.
+ * @param[in,out] out buffer to write the data
+ * @param[out] outlen length of the output buffer
  * @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.
+ * @return length of data encoded, or -1 on error
  */
-static VALUE_PAIR *fr_dhcp_vp2suboption(TALLOC_CTX *ctx, vp_cursor_t *cursor)
+static ssize_t fr_dhcp_vp2data_tlv(uint8_t *out, ssize_t outlen, vp_cursor_t *cursor)
 {
-       ssize_t length;
+       ssize_t len;
        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;
+       uint8_t *p, *opt_len;
+       vp_cursor_t tlv_cursor;
+       VALUE_PAIR *vp;
 
 #define SUBOPTION_PARENT(_x) (_x & 0xffff00ff)
 #define SUBOPTION_ATTR(_x) ((_x & 0xff00) >> 8)
 
        vp = fr_cursor_current(cursor);
-       if (!vp) return NULL;
+       if (!vp) return -1;
 
        parent = SUBOPTION_PARENT(vp->da->attr);
-       tlv = paircreate(ctx, parent, DHCP_MAGIC_VENDOR);
-       if (!tlv) return NULL;
 
-       fr_cursor_copy(&to_pack, cursor);
+       /*
+        *      Remember where we started off.
+        */
+       fr_cursor_copy(&tlv_cursor, cursor);
 
        /*
-        *  Loop over TLVs to determine how much memory we need to allocate
+        *      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.
+        *      We advanced the tlv_cursor we were passed, so if we
+        *      fail encoding, the tlv_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)) {
+       len = 0;
+       for (vp = fr_cursor_current(&tlv_cursor);
+            vp && vp->da->flags.is_tlv && (SUBOPTION_PARENT(vp->da->attr) == parent);
+            vp = fr_cursor_next(&tlv_cursor)) {
+               if (SUBOPTION_ATTR(vp->da->attr) == 0) {
+                       fr_strerror_printf("Invalid attribute number 0");
+                       return -1;
+               }
+
                /*
-                *  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 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.array || (SUBOPTION_ATTR(vp->da->attr) != attr)) {
                        attr = SUBOPTION_ATTR(vp->da->attr);
-                       tlv->vp_length += 2;
+                       len += 2;
                }
-               tlv->vp_length += vp->vp_length;
+               len += vp->vp_length;
        }
 
-       tlv->vp_tlv = talloc_zero_array(tlv, uint8_t, tlv->vp_length);
-       if (!tlv->vp_tlv) {
-               talloc_free(tlv);
-               return NULL;
+       if (len > outlen) {
+               fr_strerror_printf("Insufficient room for suboption");
+               return -1;
        }
-       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;
-               }
-
+       opt_len = NULL;
+       p = out;
+       
+       for (vp = fr_cursor_current(cursor);
+            vp && vp->da->flags.is_tlv && (SUBOPTION_PARENT(vp->da->attr) == parent);
+            vp = fr_cursor_next(cursor)) {
                /* Don't write out the header, were packing array options */
-               if (!vp->da->flags.array || (attr != SUBOPTION_ATTR(vp->da->attr))) {
+               if (!opt_len || !vp->da->flags.array || (attr != SUBOPTION_ATTR(vp->da->attr))) {
                        attr = SUBOPTION_ATTR(vp->da->attr);
                        *p++ = attr;
                        opt_len = p++;
+                       *opt_len = 0;
                }
 
-               length = fr_dhcp_vp2attr(p, (tlv->vp_tlv + tlv->vp_length) - p, vp);
-               if ((length < 0) || (length > 255)) {
-                       talloc_free(tlv);
-                       return NULL;
+               len = fr_dhcp_vp2data(p, out + outlen - p, vp);
+               if ((len < 0) || (len > 255)) {
+                       return -1;
                }
 
-               fr_assert(opt_len);
-               *opt_len += length;
-               p += length;
+               debug_pair(vp);
+               *opt_len += len;
+               p += len;
        };
 
-       return tlv;
+       return p - out;
 }
 
 /** Encode a DHCP option and any sub-options.
@@ -1414,7 +1311,7 @@ static VALUE_PAIR *fr_dhcp_vp2suboption(TALLOC_CTX *ctx, vp_cursor_t *cursor)
  * @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(TALLOC_CTX *ctx, uint8_t *out, size_t outlen, vp_cursor_t *cursor)
+ssize_t fr_dhcp_encode_option(UNUSED TALLOC_CTX *ctx, uint8_t *out, size_t outlen, vp_cursor_t *cursor)
 {
        VALUE_PAIR *vp;
        DICT_ATTR const *previous;
@@ -1450,49 +1347,34 @@ ssize_t fr_dhcp_encode_option(TALLOC_CTX *ctx, uint8_t *out, size_t outlen, vp_c
 
        /* 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;
                /*
-                *  If not calling fr_dhcp_vp2suboption() advance the cursor, so fr_cursor_current()
-                *  returns the next attribute.
+                *      Sub-option encoder will encode the data and
+                *      advance the cursor.
                 */
+               if (vp->da->flags.is_tlv) {
+                       len = fr_dhcp_vp2data_tlv(p, freespace, cursor);
+                       previous = NULL;
+
                } else {
+                       len = fr_dhcp_vp2data(p, freespace, vp);
+                       if (len >= 0) debug_pair(vp);
                        fr_cursor_next(cursor);
+                       previous = vp->da;
                }
 
-               if ((*opt_len + vp->vp_length) > 255) {
+               if (len < 0) return len;
+
+               if ((*opt_len + len) > 255) {
                        fr_strerror_printf("Skipping \"%s\": Option splitting not supported "
                                           "(option > 255 bytes)", vp->da->name);
-                       talloc_free(tlv);
                        return 0;
                }
 
-               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);
+       } while ((vp = fr_cursor_current(cursor)) && previous && (previous == vp->da) && vp->da->flags.array);
 
        return p - out;
 }
@@ -1504,6 +1386,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        vp_cursor_t cursor;
        VALUE_PAIR *vp;
        uint32_t lvalue;
+       uint16_t        svalue;
        size_t dhcp_size;
        ssize_t len;
 #ifndef NDEBUG
@@ -1523,7 +1406,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        if (packet->code == 0) packet->code = PW_DHCP_NAK;
 
        /* store xid */
-       if ((vp = pairfind(packet->vps, 260, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_num(packet->vps, 260, DHCP_MAGIC_VENDOR, TAG_ANY))) {
                packet->id = vp->vp_integer;
        } else {
                packet->id = fr_rand();
@@ -1531,7 +1414,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
 
 #ifndef NDEBUG
        if ((packet->code >= PW_DHCP_DISCOVER) &&
-           (packet->code <= PW_DHCP_INFORM)) {
+           (packet->code < (1024 + DHCP_MAX_MESSAGE_TYPE))) {
                name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
        } else {
                name = "?Unknown?";
@@ -1571,7 +1454,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
         */
 
        /* DHCP-DHCP-Maximum-Msg-Size */
-       vp = pairfind(packet->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp && (vp->vp_integer > mms)) {
                mms = vp->vp_integer;
 
@@ -1579,7 +1462,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        }
 #endif
 
-       vp = pairfind(packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                *p++ = vp->vp_integer & 0xff;
        } else {
@@ -1587,22 +1470,22 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        }
 
        /* DHCP-Hardware-Type */
-       if ((vp = pairfind(packet->vps, 257, DHCP_MAGIC_VENDOR, TAG_ANY))) {
-               *p++ = vp->vp_integer & 0xFF;
+       if ((vp = fr_pair_find_by_num(packet->vps, 257, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+               *p++ = vp->vp_byte;
        } else {
                *p++ = 1;               /* hardware type = ethernet */
        }
 
        /* DHCP-Hardware-Address-Length */
-       if ((vp = pairfind(packet->vps, 258, DHCP_MAGIC_VENDOR, TAG_ANY))) {
-               *p++ = vp->vp_integer & 0xFF;
+       if ((vp = fr_pair_find_by_num(packet->vps, 258, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+               *p++ = vp->vp_byte;
        } else {
                *p++ = 6;               /* 6 bytes of ethernet */
        }
 
        /* DHCP-Hop-Count */
-       if ((vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY))) {
-               *p = vp->vp_integer & 0xff;
+       if ((vp = fr_pair_find_by_num(packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+               *p = vp->vp_byte;
        }
        p++;
 
@@ -1612,27 +1495,27 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        p += 4;
 
        /* DHCP-Number-of-Seconds */
-       if ((vp = pairfind(packet->vps, 261, DHCP_MAGIC_VENDOR, TAG_ANY))) {
-               lvalue = htonl(vp->vp_integer);
-               memcpy(p, &lvalue, 2);
+       if ((vp = fr_pair_find_by_num(packet->vps, 261, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+               svalue = htons(vp->vp_short);
+               memcpy(p, &svalue, 2);
        }
        p += 2;
 
        /* DHCP-Flags */
-       if ((vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY))) {
-               lvalue = htons(vp->vp_integer);
-               memcpy(p, &lvalue, 2);
+       if ((vp = fr_pair_find_by_num(packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+               svalue = htons(vp->vp_short);
+               memcpy(p, &svalue, 2);
        }
        p += 2;
 
        /* DHCP-Client-IP-Address */
-       if ((vp = pairfind(packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_num(packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY))) {
                memcpy(p, &vp->vp_ipaddr, 4);
        }
        p += 4;
 
        /* DHCP-Your-IP-address */
-       if ((vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_num(packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY))) {
                lvalue = vp->vp_ipaddr;
        } else {
                lvalue = htonl(INADDR_ANY);
@@ -1641,7 +1524,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        p += 4;
 
        /* DHCP-Server-IP-Address */
-       vp = pairfind(packet->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                lvalue = vp->vp_ipaddr;
        } else {
@@ -1653,7 +1536,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        /*
         *      DHCP-Gateway-IP-Address
         */
-       if ((vp = pairfind(packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_num(packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY))) {
                lvalue = vp->vp_ipaddr;
        } else {
                lvalue = htonl(INADDR_ANY);
@@ -1662,15 +1545,22 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        p += 4;
 
        /* DHCP-Client-Hardware-Address */
-       if ((vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_num(packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY))) {
                if (vp->vp_length == sizeof(vp->vp_ether)) {
+                       /*
+                        *      Ensure that we mark the packet as being Ethernet.
+                        *      This is mainly for DHCP-Lease-Query responses.
+                        */
+                       packet->data[1] = 1;
+                       packet->data[2] = 6;
+
                        memcpy(p, vp->vp_ether, vp->vp_length);
                } /* else ignore it */
        }
        p += DHCP_CHADDR_LEN;
 
        /* DHCP-Server-Host-Name */
-       if ((vp = pairfind(packet->vps, 268, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_num(packet->vps, 268, DHCP_MAGIC_VENDOR, TAG_ANY))) {
                if (vp->vp_length > DHCP_SNAME_LEN) {
                        memcpy(p, vp->vp_strvalue, DHCP_SNAME_LEN);
                } else {
@@ -1690,7 +1580,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
         */
 
        /* DHCP-Boot-Filename */
-       vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                if (vp->vp_length > DHCP_FILE_LEN) {
                        memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN);
@@ -1708,7 +1598,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        /*
         *      Print the header.
         */
-       if (fr_debug_flag > 1) {
+       if (fr_debug_lvl > 1) {
                uint8_t *pp = p;
 
                p = packet->data;
@@ -1716,7 +1606,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
                for (i = 0; i < 14; i++) {
                        char *q;
 
-                       vp = pairmake(packet, NULL,
+                       vp = fr_pair_make(packet, NULL,
                                      dhcp_header_names[i], NULL, T_OP_EQ);
                        if (!vp) {
                                char buffer[256];
@@ -1752,7 +1642,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
                                break;
 
                        case PW_TYPE_OCTETS: /* only for Client HW Address */
-                               pairmemcpy(vp, p, packet->data[2]);
+                               fr_pair_value_memcpy(vp, p, packet->data[2]);
                                break;
 
                        case PW_TYPE_ETHERNET: /* only for Client HW Address */
@@ -1761,14 +1651,14 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
 
                        default:
                                fr_strerror_printf("Internal sanity check failed %d %d", vp->da->type, __LINE__);
-                               pairfree(&vp);
+                               fr_pair_list_free(&vp);
                                break;
                        }
 
                        p += dhcp_header_sizes[i];
 
                        debug_pair(vp);
-                       pairfree(&vp);
+                       fr_pair_list_free(&vp);
                }
 
                /*
@@ -1782,12 +1672,11 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        p[2] = packet->code - PW_DHCP_OFFSET;
        p += 3;
 
-
        /*
         *  Pre-sort attributes into contiguous blocks so that fr_dhcp_encode_option
         *  operates correctly. This changes the order of the list, but never mind...
         */
-       pairsort(&packet->vps, fr_dhcp_attr_cmp);
+       fr_pair_list_sort(&packet->vps, fr_dhcp_attr_cmp);
        fr_cursor_init(&cursor, &packet->vps);
 
        /*
@@ -1797,7 +1686,6 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        while ((vp = fr_cursor_current(&cursor))) {
                len = fr_dhcp_encode_option(packet, p, packet->data_len - (p - packet->data), &cursor);
                if (len < 0) break;
-               if (len > 0) debug_pair(vp);
                p += len;
        };
 
@@ -1827,7 +1715,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
                packet->data_len = DEFAULT_PACKET_SIZE;
        }
 
-       if ((fr_debug_flag > 2) && fr_log_fp) {
+       if ((fr_debug_lvl > 2) && fr_log_fp) {
                fprintf(fr_log_fp, "DHCP Sending %zu bytes\n", packet->data_len);
                for (i = 0; i < packet->data_len; i++) {
                        if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, "%d: ", (int) i);
@@ -1912,7 +1800,7 @@ int fr_socket_packet(int iface_index, struct sockaddr_ll *p_ll)
        if (lsockfd < 0) {
                fr_strerror_printf("cannot open socket: %s", fr_syserror(errno));
                return lsockfd;
-       } 
+       }
 
        /* Set link layer parameters */
        memset(p_ll, 0, sizeof(struct sockaddr_ll));
@@ -1923,7 +1811,7 @@ int fr_socket_packet(int iface_index, struct sockaddr_ll *p_ll)
        p_ll->sll_hatype = ARPHRD_ETHER;
        p_ll->sll_pkttype = PACKET_OTHERHOST;
        p_ll->sll_halen = 6;
-    
+
        if (bind(lsockfd, (struct sockaddr *)p_ll, sizeof(struct sockaddr_ll)) < 0) {
                close(lsockfd);
                fr_strerror_printf("cannot bind raw socket: %s", fr_syserror(errno));
@@ -1943,7 +1831,7 @@ int fr_dhcp_send_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RADIUS_PACKET
 
        /* set ethernet source address to our MAC address (DHCP-Client-Hardware-Address). */
        u_char dhmac[ETH_ADDR_LEN] = { 0 };
-       if ((vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       if ((vp = fr_pair_find_by_num(packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY))) {
                if (vp->length == sizeof(vp->vp_ether)) {
                        memcpy(dhmac, vp->vp_ether, vp->length);
                }
@@ -1959,7 +1847,7 @@ int fr_dhcp_send_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RADIUS_PACKET
        struct ip_header *iph = (struct ip_header *)(dhcp_packet + ETH_HDR_SIZE);
        iph->ip_vhl = IP_VHL(4, 5);
        iph->ip_tos = 0;
-       iph->ip_len = htons(IP_HDR_SIZE +  UDP_HDR_SIZE + packet->data_len);  
+       iph->ip_len = htons(IP_HDR_SIZE +  UDP_HDR_SIZE + packet->data_len);
        iph->ip_id = 0;
        iph->ip_off = 0;
        iph->ip_ttl = 64;
@@ -1993,14 +1881,14 @@ int fr_dhcp_send_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RADIUS_PACKET
        uh->checksum = fr_udp_checksum((uint8_t const *)(dhcp_packet + ETH_HDR_SIZE + IP_HDR_SIZE), ntohs(uh->len), uh->checksum,
                                        packet->src_ipaddr.ipaddr.ip4addr, packet->dst_ipaddr.ipaddr.ip4addr);
 
-       if (fr_debug_flag > 1) {
+       if (fr_debug_lvl > 1) {
                char type_buf[64];
                char const *name = type_buf;
                char src_ip_buf[INET6_ADDRSTRLEN];
                char dst_ip_buf[INET6_ADDRSTRLEN];
 
                if ((packet->code >= PW_DHCP_DISCOVER) &&
-                   (packet->code <= PW_DHCP_INFORM)) {
+                   (packet->code < (1024 + DHCP_MAX_MESSAGE_TYPE))) {
                        name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
                } else {
                        snprintf(type_buf, sizeof(type_buf), "%d",
@@ -2014,7 +1902,7 @@ int fr_dhcp_send_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RADIUS_PACKET
                   inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, dst_ip_buf, sizeof(dst_ip_buf)), packet->dst_port);
        }
 
-       return sendto(sockfd, dhcp_packet, 
+       return sendto(sockfd, dhcp_packet,
                (ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE + packet->data_len),
                0, (struct sockaddr *) p_ll, sizeof(struct sockaddr_ll));
 }
@@ -2089,13 +1977,13 @@ RADIUS_PACKET *fr_dhcp_recv_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RAD
         * Check if it matches the source HW address used (DHCP-Client-Hardware-Address = 267)
         */
        if ( (memcmp(&eth_bcast, &eth_hdr->ether_dst, ETH_ADDR_LEN) != 0) &&
-                       (vp = pairfind(request->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY)) &&
+                       (vp = fr_pair_find_by_num(request->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY)) &&
                        (vp->length == sizeof(vp->vp_ether)) &&
                        (memcmp(vp->vp_ether, &eth_hdr->ether_dst, ETH_ADDR_LEN) != 0) ) {
                /* No match. */
                char eth_dest[17+1];
                char eth_req_src[17+1];
-               DISCARD_RP("Ethernet destination (%s) is not broadcast and doesn't match request source (%s)", 
+               DISCARD_RP("Ethernet destination (%s) is not broadcast and doesn't match request source (%s)",
                        ether_addr_print(eth_hdr->ether_dst, eth_dest),
                        ether_addr_print(vp->vp_ether, eth_req_src));
        }
@@ -2136,7 +2024,7 @@ RADIUS_PACKET *fr_dhcp_recv_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RAD
 
        if (dhcp_data_len < MIN_PACKET_SIZE) DISCARD_RP("DHCP packet is too small (%d < %d)", dhcp_data_len, MIN_PACKET_SIZE);
        if (dhcp_data_len > MAX_PACKET_SIZE) DISCARD_RP("DHCP packet is too large (%d > %d)", dhcp_data_len, MAX_PACKET_SIZE);
-       
+
        dhcp_hdr = (dhcp_packet_t *)(raw_packet + ETH_HDR_SIZE + IP_HDR_SIZE + UDP_HDR_SIZE);
 
        if (dhcp_hdr->htype != 1) DISCARD_RP("DHCP hardware type (%d) != Ethernet (1)", dhcp_hdr->htype);
@@ -2197,13 +2085,13 @@ RADIUS_PACKET *fr_dhcp_recv_raw_packet(int sockfd, struct sockaddr_ll *p_ll, RAD
        packet->dst_ipaddr.af = AF_INET;
        packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip_hdr->ip_dst.s_addr;
 
-       if (fr_debug_flag > 1) {
+       if (fr_debug_lvl > 1) {
                char type_buf[64];
                char const *name = type_buf;
                char src_ip_buf[256], dst_ip_buf[256];
 
                if ((packet->code >= PW_DHCP_DISCOVER) &&
-                   (packet->code <= PW_DHCP_INFORM)) {
+                   (packet->code < (1024 + DHCP_MAX_MESSAGE_TYPE))) {
                        name = dhcp_message_types[packet->code - PW_DHCP_OFFSET];
                } else {
                        snprintf(type_buf, sizeof(type_buf), "%d", packet->code - PW_DHCP_OFFSET);
index 74a6501..5c923f1 100644 (file)
@@ -26,7 +26,6 @@ RCSID("$Id$")
 
 #include <freeradius-devel/libradius.h>
 #include <freeradius-devel/conf.h>
-#include <freeradius-devel/radpaths.h>
 #include <freeradius-devel/dhcp.h>
 
 #ifdef WITH_DHCP
@@ -46,26 +45,15 @@ static int retries = 3;
 static float timeout = 5.0;
 static struct timeval tv_timeout;
 
-static uint16_t server_port = 0;
-static int packet_code = 0;
-static fr_ipaddr_t server_ipaddr;
-
-static fr_ipaddr_t client_ipaddr;
-static uint16_t client_port = 0;
-
 static int sockfd;
 
-#ifdef HAVE_LINUX_IF_PACKET_H
-struct sockaddr_ll ll; /* Socket address structure */
 static char *iface = NULL;
 static int iface_ind = -1;
 
-#  define DEBUG                        if (fr_debug_flag && fr_log_fp) fr_printf_log
-#endif
-
-static RADIUS_PACKET *request = NULL;
 static RADIUS_PACKET *reply = NULL;
 
+static bool reply_expected = true;
+
 #define DHCP_CHADDR_LEN        (16)
 #define DHCP_SNAME_LEN (64)
 #define DHCP_FILE_LEN  (128)
@@ -82,18 +70,27 @@ typedef struct dc_offer {
        uint32_t offered_addr;
 } dc_offer_t;
 
+static const FR_NAME_NUMBER request_types[] = {
+       { "discover", PW_DHCP_DISCOVER },
+       { "request",  PW_DHCP_REQUEST },
+       { "decline",  PW_DHCP_DECLINE },
+       { "release",  PW_DHCP_RELEASE },
+       { "inform",   PW_DHCP_INFORM },
+       { "lease_query",  PW_DHCP_LEASE_QUERY },
+       { "auto",     PW_CODE_UNDEFINED },
+       { NULL, 0}
+};
+
 static void NEVER_RETURNS usage(void)
 {
-       fprintf(stderr, "Usage: dhcpclient [options] server[:port] <command>\n");
+       fprintf(stderr, "Usage: dhcpclient [options] server[:port] [<command>]\n");
        fprintf(stderr, "Send a DHCP request with provided RADIUS attrs and output response.\n");
 
-       fprintf(stderr, "  <command>              One of discover, request, offer, decline, release, inform.\n");
+       fprintf(stderr, "  <command>              One of: discover, request, decline, release, inform; or: auto.\n");
        fprintf(stderr, "  -d <directory>         Set the directory where the dictionaries are stored (defaults to " RADDBDIR ").\n");
        fprintf(stderr, "  -D <dictdir>           Set main dictionary directory (defaults to " DICTDIR ").\n");
        fprintf(stderr, "  -f <file>              Read packets from file, not stdin.\n");
-#ifdef HAVE_LINUX_IF_PACKET_H
        fprintf(stderr, "  -i <interface>         Use this interface to send/receive at packet level on a raw socket.\n");
-#endif
        fprintf(stderr, "  -t <timeout>           Wait 'timeout' seconds for a reply (may be a floating point number).\n");
        fprintf(stderr, "  -v                     Show program version information.\n");
        fprintf(stderr, "  -x                     Debugging mode.\n");
@@ -105,12 +102,13 @@ static void NEVER_RETURNS usage(void)
 /*
  *     Initialize the request.
  */
-static int request_init(char const *filename)
+static RADIUS_PACKET *request_init(char const *filename)
 {
        FILE *fp;
        vp_cursor_t cursor;
        VALUE_PAIR *vp;
        bool filedone = false;
+       RADIUS_PACKET *request;
 
        /*
         *      Determine where to read the VP's from.
@@ -118,38 +116,41 @@ static int request_init(char const *filename)
        if (filename) {
                fp = fopen(filename, "r");
                if (!fp) {
-                       fprintf(stderr, "dhcpclient: Error opening %s: %s\n",
-                               filename, fr_syserror(errno));
-                       return 0;
+                       fprintf(stderr, "dhcpclient: Error opening %s: %s\n", filename, fr_syserror(errno));
+                       return NULL;
                }
        } else {
                fp = stdin;
        }
 
        request = rad_alloc(NULL, false);
-
        /*
         *      Read the VP's.
         */
-       if (readvp2(NULL, &request->vps, fp, &filedone) < 0) {
+       if (fr_pair_list_afrom_file(NULL, &request->vps, fp, &filedone) < 0) {
                fr_perror("dhcpclient");
                rad_free(&request);
                if (fp != stdin) fclose(fp);
-               return 1;
+               return NULL;
        }
 
        /*
         *      Fix / set various options
         */
-       for (vp = fr_cursor_init(&cursor, &request->vps); vp; vp = fr_cursor_next(&cursor)) {
-               switch (vp->da->attr) {
-               default:
-                       break;
-
-                       /*
-                        *      Allow it to set the packet type in
-                        *      the attributes read from the file.
-                        */
+       for (vp = fr_cursor_init(&cursor, &request->vps);
+            vp;
+            vp = fr_cursor_next(&cursor)) {
+               /*
+                *      Allow to set packet type using DHCP-Message-Type
+                */
+               if (vp->da->vendor == DHCP_MAGIC_VENDOR && vp->da->attr == PW_DHCP_MESSAGE_TYPE) {
+                       request->code = vp->vp_integer + PW_DHCP_OFFSET;
+               } else if (!vp->da->vendor) switch (vp->da->attr) {
+               /*
+                *      Allow it to set the packet type in
+                *      the attributes read from the file.
+                *      (this takes precedence over the command argument.)
+                */
                case PW_PACKET_TYPE:
                        request->code = vp->vp_integer;
                        break;
@@ -185,6 +186,9 @@ static int request_init(char const *filename)
                        request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
                        request->src_ipaddr.prefix = 128;
                        break;
+
+               default:
+                       break;
                } /* switch over the attribute */
 
        } /* loop over the VP's we read in */
@@ -194,7 +198,7 @@ static int request_init(char const *filename)
        /*
         *      And we're done.
         */
-       return 1;
+       return request;
 }
 
 static char const *dhcp_header_names[] = {
@@ -283,118 +287,59 @@ static void print_hex(RADIUS_PACKET *packet)
        fflush(stdout);
 }
 
-#ifdef HAVE_LINUX_IF_PACKET_H
-/*
- *     Loop waiting for DHCP replies until timer expires.
- *     Note that there may be more than one reply: multiple DHCP servers can respond to a broadcast discover.
- *     A real client would pick one of the proposed replies.
- *     We'll just return the first eligible reply, and display the others.
- */
-static RADIUS_PACKET *fr_dhcp_recv_raw_loop(int lsockfd, struct sockaddr_ll *p_ll, RADIUS_PACKET *request_p)
+static void send_with_socket(RADIUS_PACKET *request)
 {
-       struct timeval tval;
-       RADIUS_PACKET *reply_p = NULL;
-       RADIUS_PACKET *cur_reply_p = NULL;
-       int nb_reply = 0;
-       int nb_offer = 0;
-       dc_offer_t *offer_list = NULL;
-       fd_set read_fd;
-       int retval;
-
-       memcpy(&tval, &tv_timeout, sizeof(struct timeval));
-
-       /* Loop waiting for DHCP replies until timer expires */
-       while (timerisset(&tval)) {
-               if ((!reply_p) || (cur_reply_p)) { // only debug at start and each time we get a valid DHCP reply on raw socket
-                       DEBUG("Waiting for%sDHCP replies for: %d.%06d\n", 
-                               (nb_reply>0)?" additional ":" ", (int)tval.tv_sec, (int)tval.tv_usec);
-               }
+       sockfd = fr_socket(&request->src_ipaddr, request->src_port);
+       if (sockfd < 0) {
+               fprintf(stderr, "dhcpclient: socket: %s\n", fr_strerror());
+               fr_exit_now(1);
+       }
 
-               cur_reply_p = NULL;
-               FD_ZERO(&read_fd);
-               FD_SET(lsockfd, &read_fd);
-               retval = select(lsockfd + 1, &read_fd, NULL, NULL, &tval);
+       /*
+        *      Set option 'receive timeout' on socket.
+        *      Note: in case of a timeout, the error will be "Resource temporarily unavailable".
+        */
+       if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_timeout,sizeof(struct timeval)) == -1) {
+               fprintf(stderr, "dhcpclient: failed setting socket timeout: %s\n",
+                       fr_syserror(errno));
+               fr_exit_now(1);
+       }
 
-               if (retval < 0) {
-                       fr_strerror_printf("Select on DHCP socket failed: %s", fr_syserror(errno));
-                       return NULL;
-               }
+       request->sockfd = sockfd;
 
-               if ( retval > 0 && FD_ISSET(lsockfd, &read_fd)) {
-                       /* There is something to read on our socket */
-                       cur_reply_p = fr_dhcp_recv_raw_packet(lsockfd, p_ll, request_p);
-               }
-               
-               if (cur_reply_p) {
-                       nb_reply ++;
-                       
-                       if (fr_debug_flag) print_hex(cur_reply_p);
-
-                       if (fr_dhcp_decode(cur_reply_p) < 0) {
-                               fprintf(stderr, "dhcpclient: failed decoding reply\n");
-                               return NULL;
-                       }
-                       
-                       if (!reply_p) reply_p = cur_reply_p;
-                       
-                       if (cur_reply_p->code == PW_DHCP_OFFER) {
-                               VALUE_PAIR *vp1 = pairfind(cur_reply_p->vps, 54,  DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-DHCP-Server-Identifier */
-                               VALUE_PAIR *vp2 = pairfind(cur_reply_p->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-address */
-                               
-                               if (vp1 && vp2) {
-                                       nb_offer ++;
-                                       offer_list = talloc_realloc(request_p, offer_list, dc_offer_t, nb_offer);
-                                       offer_list[nb_offer-1].server_addr = vp1->vp_ipaddr;
-                                       offer_list[nb_offer-1].offered_addr = vp2->vp_ipaddr;
-                               }
-                       }
-               }
-       }
-       
-       if (0 == nb_reply) {
-               fr_strerror_printf("No valid DHCP reply received");
-               return NULL;
+       if (fr_dhcp_send(request) < 0) {
+               fprintf(stderr, "dhcpclient: failed sending: %s\n",
+                       fr_syserror(errno));
+               fr_exit_now(1);
        }
-       
-       /* display offer(s) received */
-       if (nb_offer > 0 ) {
-               DEBUG("Received %d DHCP Offer(s):\n", nb_offer);
-               int i;
-               for (i=0; i<nb_reply; i++) {
-                       char server_addr_buf[INET6_ADDRSTRLEN];
-                       char offered_addr_buf[INET6_ADDRSTRLEN];
-                       
-                       DEBUG("IP address: %s offered by DHCP server: %s\n",
-                               inet_ntop(AF_INET, &offer_list[i].offered_addr, offered_addr_buf, sizeof(offered_addr_buf)),
-                               inet_ntop(AF_INET, &offer_list[i].server_addr, server_addr_buf, sizeof(server_addr_buf))
-                       );
-               }
+
+       if (!reply_expected) return;
+
+       reply = fr_dhcp_recv(sockfd);
+       if (!reply) {
+               fprintf(stderr, "dhcpclient: Error receiving reply: %s\n", fr_strerror());
+               fr_exit_now(1);
        }
-       
-       return reply_p;
 }
-#endif
 
 int main(int argc, char **argv)
 {
-       char *p;
-       int c;
-       char const *radius_dir = RADDBDIR;
-       char const *dict_dir = DICTDIR;
-       char const *filename = NULL;
-       DICT_ATTR const *da;
-
-#ifdef HAVE_LINUX_IF_PACKET_H
-       bool raw_mode = false;
-#endif
 
-       fr_debug_flag = 0;
+       static uint16_t         server_port = 0;
+       static int              packet_code = 0;
+       static fr_ipaddr_t      server_ipaddr;
+       static fr_ipaddr_t      client_ipaddr;
 
-       while ((c = getopt(argc, argv, "d:D:f:hr:t:vx"
-#ifdef HAVE_LINUX_IF_PACKET_H
-               "i:"
-#endif
-       )) != EOF) switch(c) {
+       int                     c;
+       char const              *radius_dir = RADDBDIR;
+       char const              *dict_dir = DICTDIR;
+       char const              *filename = NULL;
+       DICT_ATTR const         *da;
+       RADIUS_PACKET           *request = NULL;
+
+       fr_debug_lvl = 0;
+
+       while ((c = getopt(argc, argv, "d:D:f:hr:t:vxi:")) != EOF) switch(c) {
                case 'D':
                        dict_dir = optarg;
                        break;
@@ -405,11 +350,9 @@ int main(int argc, char **argv)
                case 'f':
                        filename = optarg;
                        break;
-#ifdef HAVE_LINUX_IF_PACKET_H
                case 'i':
                        iface = optarg;
                        break;
-#endif
                case 'r':
                        if (!isdigit((int) *optarg))
                                usage();
@@ -426,7 +369,7 @@ int main(int argc, char **argv)
                        exit(0);
 
                case 'x':
-                       fr_debug_flag++;
+                       fr_debug_lvl++;
                        fr_log_fp = stdout;
                        break;
                case 'h':
@@ -443,10 +386,16 @@ int main(int argc, char **argv)
        tv_timeout.tv_sec = timeout;
        tv_timeout.tv_usec = ((timeout - (float) tv_timeout.tv_sec) * USEC);
 
-       if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
-               fr_perror("dhcpclient");
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("radclient");
+               return 1;
+       }
+
+       if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
+               fr_perror("radclient");
                return 1;
        }
+       fr_strerror();  /* Clear the error buffer */
 
        /*
         *      Ensure that dictionary.dhcp is loaded.
@@ -454,14 +403,7 @@ int main(int argc, char **argv)
        da = dict_attrbyname("DHCP-Message-Type");
        if (!da) {
                if (dict_read(dict_dir, "dictionary.dhcp") < 0) {
-                       fprintf(stderr, "Failed reading dictionary.dhcp: %s",
-                               fr_strerror());
-                       return -1;
-               }
-
-               if (dict_read(dict_dir, "dictionary.freeradius.internal") < 0) {
-                       fprintf(stderr, "Failed reading dictionary.freeradius.internal: %s",
-                               fr_strerror());
+                       fprintf(stderr, "Failed reading dictionary.dhcp: %s\n", fr_strerror());
                        return -1;
                }
        }
@@ -471,193 +413,90 @@ int main(int argc, char **argv)
         */
        server_ipaddr.af = AF_INET;
        if (strcmp(argv[1], "-") != 0) {
-               char const *hostname = argv[1];
-               char const *portname = argv[1];
-               char buffer[256];
-
-               if (*argv[1] == '[') { /* IPv6 URL encoded */
-                       p = strchr(argv[1], ']');
-                       if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
-                               usage();
-                       }
-
-                       memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
-                       buffer[p - argv[1] - 1] = '\0';
-
-                       hostname = buffer;
-                       portname = p + 1;
-
-               }
-               p = strchr(portname, ':');
-               if (p && (strchr(p + 1, ':') == NULL)) {
-                       *p = '\0';
-                       portname = p + 1;
-               } else {
-                       portname = NULL;
+               if (ip_hton(&server_ipaddr, AF_INET, argv[1], false) < 0) {
+                       fr_perror("dhcpclient");
+                       fr_exit_now(1);
                }
-
-               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);
-               }
-
-               /*
-                *      Strip port from hostname if needed.
-                */
-               if (portname) server_port = atoi(portname);
+               client_ipaddr.af = server_ipaddr.af;
        }
 
        /*
         *      See what kind of request we want to send.
         */
-       if (strcmp(argv[2], "discover") == 0) {
-               if (server_port == 0) server_port = 67;
-               packet_code = PW_DHCP_DISCOVER;
-
-       } else if (strcmp(argv[2], "request") == 0) {
-               if (server_port == 0) server_port = 67;
-               packet_code = PW_DHCP_REQUEST;
-
-       } else if (strcmp(argv[2], "offer") == 0) {
-               if (server_port == 0) server_port = 67;
-               packet_code = PW_DHCP_OFFER;
-
-       } else if (strcmp(argv[2], "decline") == 0) {
-               if (server_port == 0) server_port = 67;
-               packet_code = PW_DHCP_DECLINE;
-
-       } else if (strcmp(argv[2], "release") == 0) {
-               if (server_port == 0) server_port = 67;
-               packet_code = PW_DHCP_RELEASE;
-
-       } else if (strcmp(argv[2], "inform") == 0) {
-               if (server_port == 0) server_port = 67;
-               packet_code = PW_DHCP_INFORM;
-
-       } else if (isdigit((int) argv[2][0])) {
-               if (server_port == 0) server_port = 67;
-               packet_code = atoi(argv[2]);
-       } else {
-               fprintf(stderr, "Unknown packet type %s\n", argv[2]);
-               usage();
-       }
-
-       request_init(filename);
-
-       /*
-        *      No data read.  Die.
-        */
-       if (!request || !request->vps) {
-               fprintf(stderr, "dhcpclient: Nothing to send.\n");
-               exit(1);
+       if (argc >= 3) {
+               if (!isdigit((int) argv[2][0])) {
+                       packet_code = fr_str2int(request_types, argv[2], -2);
+                       if (packet_code == -2) {
+                               fprintf(stderr, "Unknown packet type: %s\n", argv[2]);
+                               usage();
+                       }
+               } else {
+                       packet_code = atoi(argv[2]);
+               }
        }
-       request->code = packet_code;
+       if (!server_port) server_port = 67;
 
        /*
-        *      Bind to the first specified IP address and port.
-        *      This means we ignore later ones.
+        *      set "raw mode" if an interface is specified and if destination
+        *      IP address is the broadcast address.
         */
-       if (request->src_ipaddr.af == AF_UNSPEC) {
-               memset(&client_ipaddr, 0, sizeof(client_ipaddr));
-               client_ipaddr.af = server_ipaddr.af;
-               client_port = 0;
-       } else {
-               client_ipaddr = request->src_ipaddr;
-               client_port = request->src_port;
-       }
-       
-       /* set "raw mode" if an interface is specified and if destination IP address is the broadcast address. */
-#ifdef HAVE_LINUX_IF_PACKET_H
        if (iface) {
                iface_ind = if_nametoindex(iface);
                if (iface_ind <= 0) {
                        fprintf(stderr, "dhcpclient: unknown interface: %s\n", iface);
-                       exit(1);
+                       fr_exit_now(1);
                }
 
                if (server_ipaddr.ipaddr.ip4addr.s_addr == 0xFFFFFFFF) {
-                       DEBUG("dhcpclient: Using interface: %s (index: %d) in raw packet mode\n", iface, iface_ind);
-                       raw_mode = true;
+                       fprintf(stderr, "dhcpclient: Sending broadcast packets is not supported\n");
+                       exit(1);
                }
        }
 
-       if (raw_mode) {
-               sockfd = fr_socket_packet(iface_ind, &ll);
-       } else
-#endif
-
-       {
-               sockfd = fr_socket(&client_ipaddr, client_port);
+       request = request_init(filename);
+       if (!request || !request->vps) {
+               fprintf(stderr, "dhcpclient: Nothing to send.\n");
+               fr_exit_now(1);
        }
 
-       if (sockfd < 0) {
-               fprintf(stderr, "dhcpclient: socket: %s\n", fr_strerror());
-               exit(1);
-       }
+       /*
+        *      Set defaults if they weren't specified via pairs
+        */
+       if (request->src_port == 0) request->src_port = server_port + 1;
+       if (request->dst_port == 0) request->dst_port = server_port;
+       if (request->src_ipaddr.af == AF_UNSPEC) request->src_ipaddr = client_ipaddr;
+       if (request->dst_ipaddr.af == AF_UNSPEC) request->dst_ipaddr = server_ipaddr;
+       if (!request->code) request->code = packet_code;
 
        /*
-        *      Set option 'receive timeout' on socket.
-        *      Note: in case of a timeout, the error will be "Resource temporarily unavailable".
+        *      Sanity check.
         */
-       if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv_timeout,sizeof(struct timeval)) == -1) {
-               fprintf(stderr, "dhcpclient: failed setting socket timeout: %s\n",
-                       fr_syserror(errno));
+       if (!request->code) {
+               fprintf(stderr, "dhcpclient: Command was %s, and request did not contain DHCP-Message-Type nor Packet-Type.\n",
+                       (argc >= 3) ? "'auto'" : "unspecified");
                exit(1);
        }
 
-       request->sockfd = sockfd;
-       if (request->src_ipaddr.af == AF_UNSPEC) {
-               request->src_ipaddr = client_ipaddr;
-               request->src_port = client_port;
+       if ((request->code == PW_DHCP_RELEASE) || (request->code == PW_DHCP_DECLINE)) {
+               /*      These kind of packets do not get a reply, so don't wait for one. */
+               reply_expected = false;
        }
-       if (request->dst_ipaddr.af == AF_UNSPEC) {
-               request->dst_ipaddr = server_ipaddr;
-               request->dst_port = server_port;
-       }
-
        /*
         *      Encode the packet
         */
        if (fr_dhcp_encode(request) < 0) {
-               fprintf(stderr, "dhcpclient: failed encoding: %s\n",
-                       fr_strerror());
-               exit(1);
+               fprintf(stderr, "dhcpclient: failed encoding: %s\n", fr_strerror());
+               fr_exit_now(1);
        }
-       if (fr_debug_flag) print_hex(request);
+       if (fr_debug_lvl) print_hex(request);
 
-#ifdef HAVE_LINUX_IF_PACKET_H
-       if (raw_mode) {
-               if (fr_dhcp_send_raw_packet(sockfd, &ll, request) < 0) {
-                       fprintf(stderr, "dhcpclient: failed sending (fr_dhcp_send_raw_packet): %s\n",
-                               fr_syserror(errno));
-                       exit(1);
-               }
-               
-               reply = fr_dhcp_recv_raw_loop(sockfd, &ll, request);
-               if (!reply) {
-                       fprintf(stderr, "dhcpclient: Error receiving reply (fr_dhcp_recv_raw_loop)\n");
-                       exit(1);
-               }
-       } else
-#endif
-       {
-               if (fr_dhcp_send(request) < 0) {
-                       fprintf(stderr, "dhcpclient: failed sending: %s\n",
-                               fr_syserror(errno));
-                       exit(1);
-               }
+       send_with_socket(request);
 
-               reply = fr_dhcp_recv(sockfd);
-               if (!reply) {
-                       fprintf(stderr, "dhcpclient: Error receiving reply %s\n", fr_strerror());
-                       exit(1);
-               }
-               if (fr_debug_flag) print_hex(reply);
+       if (reply && fr_debug_lvl) print_hex(reply);
 
-               if (fr_dhcp_decode(reply) < 0) {
-                       fprintf(stderr, "dhcpclient: failed decoding\n");
-                       return 1;
-               }
+       if (reply && fr_dhcp_decode(reply) < 0) {
+               fprintf(stderr, "dhcpclient: failed decoding\n");
+               return 1;
        }
 
        dict_free();
index 4528eaf..b6aeee5 100644 (file)
@@ -88,9 +88,9 @@ static int dhcprelay_process_client_request(REQUEST *request)
        /*
         * It's invalid to have giaddr=0 AND a relay option
         */
-       giaddr = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
+       giaddr = fr_pair_find_by_num(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 */
+           fr_pair_find_by_num(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;
        }
@@ -100,10 +100,10 @@ static int dhcprelay_process_client_request(REQUEST *request)
         *
         * Drop requests if hop-count > 16 or admin specified another value
         */
-       if ((vp = pairfind(request->config_items, 271, DHCP_MAGIC_VENDOR, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */
+       if ((vp = fr_pair_find_by_num(request->config, 271, DHCP_MAGIC_VENDOR, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */
            maxhops = vp->vp_integer;
        }
-       vp = pairfind(request->packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Hop-Count */
+       vp = fr_pair_find_by_num(request->packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Hop-Count */
        rad_assert(vp != NULL);
        if (vp->vp_integer > maxhops) {
                DEBUG("DHCP: Number of hops is greater than %d: not relaying\n", maxhops);
@@ -124,7 +124,7 @@ static int dhcprelay_process_client_request(REQUEST *request)
        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 */
+       vp = fr_pair_find_by_num(request->config, 270, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-To-IP-Address */
        rad_assert(vp != NULL);
 
        /* set DEST ipaddr/port to the next server ipaddr/port */
@@ -162,7 +162,7 @@ static int dhcprelay_process_server_reply(REQUEST *request)
        /*
         * Check that packet is for us.
         */
-       giaddr = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
+       giaddr = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
 
        /* --with-udpfromto is needed just for the following test */
        if (!giaddr || giaddr->vp_ipaddr != request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr) {
@@ -186,9 +186,9 @@ static int dhcprelay_process_server_reply(REQUEST *request)
 
        if ((request->packet->code == PW_DHCP_NAK) ||
            !sock->src_interface ||
-           ((vp = pairfind(request->packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Flags */ &&
+           ((vp = fr_pair_find_by_num(request->packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Flags */ &&
             (vp->vp_integer & 0x8000) &&
-            ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
+            ((vp = fr_pair_find_by_num(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
              (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
                /*
                 * RFC 2131, page 23
@@ -208,11 +208,11 @@ static int dhcprelay_process_server_reply(REQUEST *request)
                 * - ciaddr if present
                 * otherwise to yiaddr
                 */
-               if ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
+               if ((vp = fr_pair_find_by_num(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
                    (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                        request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                } else {
-                       vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
+                       vp = fr_pair_find_by_num(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
                        if (!vp) {
                                DEBUG("DHCP: Failed to find IP Address for request");
                                return -1;
@@ -228,7 +228,7 @@ static int dhcprelay_process_server_reply(REQUEST *request)
                         * the client was requesting an IP address.
                         */
                        if (request->packet->code == PW_DHCP_OFFER) {
-                               VALUE_PAIR *hwvp = pairfind(request->packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
+                               VALUE_PAIR *hwvp = fr_pair_find_by_num(request->packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
                                if (hwvp == NULL) {
                                        DEBUG("DHCP: DHCP_OFFER packet received with "
                                            "no Client Hardware Address. Discarding packet");
@@ -289,17 +289,17 @@ static int dhcp_process(REQUEST *request)
         *      in the response.  That way the later code knows where
         *      to send the reply.
         */
-       vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
+       vp = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
        if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                VALUE_PAIR *relay;
 
                /* DHCP-Relay-IP-Address */
-               relay = radius_paircreate(request->reply, &request->reply->vps,
+               relay = radius_pair_create(request->reply, &request->reply->vps,
                                          272, DHCP_MAGIC_VENDOR);
                if (relay) relay->vp_ipaddr = vp->vp_ipaddr;
        }
 
-       vp = pairfind(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
+       vp = fr_pair_find_by_num(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
        if (vp) {
                DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_integer);
                DEBUG("Trying sub-section dhcp %s {...}",
@@ -310,7 +310,7 @@ static int dhcp_process(REQUEST *request)
                rcode = RLM_MODULE_FAIL;
        }
 
-       vp = pairfind(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
+       vp = fr_pair_find_by_num(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
        if (vp) {
                request->reply->code = vp->vp_integer;
                if ((request->reply->code != 0) &&
@@ -359,7 +359,7 @@ static int dhcp_process(REQUEST *request)
        /*
         *      Handle requests when acting as a DHCP relay
         */
-       vp = pairfind(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
+       vp = fr_pair_find_by_num(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
        if (!vp) {
                RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!");
                return 1;
@@ -371,7 +371,7 @@ static int dhcp_process(REQUEST *request)
        }
 
        /* Packet from client, and we have DHCP-Relay-To-IP-Address */
-       if (pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) {
+       if (fr_pair_find_by_num(request->config, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) {
                return dhcprelay_process_client_request(request);
        }
 
@@ -404,15 +404,15 @@ static int dhcp_process(REQUEST *request)
        for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) {
                uint32_t attr = attrnums[i];
 
-               if (pairfind(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue;
+               if (fr_pair_find_by_num(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue;
 
-               vp = pairfind(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY);
                if (vp) {
-                       pairadd(&request->reply->vps, paircopyvp(request->reply, vp));
+                       fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
                }
        }
 
-       vp = pairfind(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
+       vp = fr_pair_find_by_num(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
        rad_assert(vp != NULL);
        vp->vp_integer = 2; /* BOOTREPLY */
 
@@ -420,7 +420,7 @@ static int dhcp_process(REQUEST *request)
         *      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);
+               vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY);
                if (vp) {
                        if (vp->vp_integer <= 10) {
                                request->response_delay.tv_sec = vp->vp_integer;
@@ -431,7 +431,7 @@ static int dhcp_process(REQUEST *request)
                        }
                } else {
 #define USEC 1000000
-                       vp = pairfind(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY_USEC, 0, TAG_ANY);
+                       vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY_USEC, 0, TAG_ANY);
                        if (vp) {
                                if (vp->vp_integer <= 10 * USEC) {
                                        request->response_delay.tv_sec = vp->vp_integer / USEC;
@@ -449,22 +449,36 @@ static int dhcp_process(REQUEST *request)
         */
        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;
        request->reply->src_ipaddr.prefix = 32;
 
        /*
-        *      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.
+        *      Packet-Src-IP-Address has highest precedence
         */
-       if (request->reply->src_ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) {
-               vp = pairfind(request->reply->vps, PW_PACKET_SRC_IP_ADDRESS, 0, TAG_ANY);
-               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;
-               }
+       vp = fr_pair_find_by_num(request->reply->vps, PW_PACKET_SRC_IP_ADDRESS, 0, TAG_ANY);
+       if (vp) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+       /*
+        *      The request was unicast (via a relay)
+        */
+       } else if (request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_BROADCAST) &&
+                  request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_ANY)) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+       /*
+        *      The listener was bound to an IP address, or we determined
+        *      the address automatically, as it was the only address bound
+        *      to the interface, and we bound to the interface.
+        */
+       } else if (sock->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_ANY)) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr;
+       /*
+        *      There's a Server-Identification attribute
+        */
+       } else if ((vp = fr_pair_find_by_num(request->reply->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+       } else {
+               REDEBUG("Unable to determine correct src_ipaddr for response");
+               return -1;
        }
-
        request->reply->dst_port = request->packet->src_port;
        request->reply->src_port = request->packet->dst_port;
 
@@ -475,10 +489,15 @@ static int dhcp_process(REQUEST *request)
         *      packet to the client.  i.e. the relay may have a
         *      public IP, but the gateway a private one.
         */
-       vp = pairfind(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */
+       vp = fr_pair_find_by_num(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */
        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;
+               request->reply->dst_port = request->packet->dst_port;
+
+               vp = fr_pair_find_by_num(request->reply->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
+               if (vp) request->reply->dst_port = vp->vp_integer;
+
                return 1;
        }
 
@@ -491,7 +510,7 @@ static int dhcp_process(REQUEST *request)
         *      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 */
+       vp = fr_pair_find_by_num(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;
@@ -504,9 +523,9 @@ static int dhcp_process(REQUEST *request)
         *      there's no client-ip-address, send a broadcast.
         */
        if ((request->reply->code == PW_DHCP_NAK) ||
-           ((vp = pairfind(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */
+           ((vp = fr_pair_find_by_num(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */
             (vp->vp_integer & 0x8000) &&
-            ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
+            ((vp = fr_pair_find_by_num(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
              (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
                /*
                 * RFC 2131, page 23
@@ -526,14 +545,14 @@ static int dhcp_process(REQUEST *request)
         *
         *      Unicast to ciaddr if present, otherwise to yiaddr.
         */
-       if ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
+       if ((vp = fr_pair_find_by_num(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
            (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                RDEBUG("DHCP: Reply will be sent unicast to client-ip-address");
                request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                return 1;
        }
 
-       vp = pairfind(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
+       vp = fr_pair_find_by_num(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
        if (!vp) {
                RDEBUG("DHCP: Failed to find DHCP-Client-IP-Address or DHCP-Your-IP-Address for request; "
                       "not responding");
@@ -575,7 +594,7 @@ static int dhcp_process(REQUEST *request)
         *      socket to send DHCP packets.
         */
        if (request->reply->code == PW_DHCP_OFFER) {
-               VALUE_PAIR *hwvp = pairfind(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
+               VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
 
                if (!hwvp) return -1;
 
@@ -605,6 +624,12 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        RADCLIENT *client;
        CONF_PAIR *cp;
 
+       /*
+        *      Set if before parsing, so the user can forcibly turn
+        *      it off later.
+        */
+       this->nodup = true;
+
        rcode = common_socket_parse(cs, this);
        if (rcode != 0) return rcode;
 
@@ -757,11 +782,13 @@ static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
 
 extern fr_protocol_t proto_dhcp;
 fr_protocol_t proto_dhcp = {
-       RLM_MODULE_INIT,
-       "dhcp",
-       sizeof(dhcp_socket_t),
-       NULL,
-       dhcp_socket_parse, NULL,
-       dhcp_socket_recv, dhcp_socket_send,
-       common_socket_print, dhcp_socket_encode, dhcp_socket_decode
+       .magic          = RLM_MODULE_INIT,
+       .name           = "dhcp",
+       .inst_size      = sizeof(dhcp_socket_t),
+       .parse          = dhcp_socket_parse,
+       .recv           = dhcp_socket_recv,
+       .send           = dhcp_socket_send,
+       .print          = common_socket_print,
+       .encode         = dhcp_socket_encode,
+       .decode         = dhcp_socket_decode
 };
diff --git a/src/modules/proto_dhcp/libfreeradius-dhcp.mk b/src/modules/proto_dhcp/libfreeradius-dhcp.mk
new file mode 100644 (file)
index 0000000..ab2cfc4
--- /dev/null
@@ -0,0 +1,3 @@
+TARGET         := libfreeradius-dhcp.a
+
+SOURCES                := dhcp.c
index ba888bc..c0baeca 100644 (file)
@@ -4,4 +4,6 @@ ifneq "$(TARGETNAME)" ""
 TARGET         := $(TARGETNAME).a
 endif
 
-SOURCES                := dhcpd.c dhcp.c
+SOURCES                := dhcpd.c
+
+TGT_PREREQS    := libfreeradius-dhcp.a
index b0d70c1..8fffa76 100644 (file)
@@ -51,34 +51,57 @@ typedef struct rlm_dhcp_t {
 static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
                                 char const *fmt, char *out, size_t freespace)
 {
-       vp_cursor_t cursor;
-       VALUE_PAIR *vp, *head = NULL;
-       int decoded = 0;
+       vp_cursor_t     cursor, src_cursor;
+       vp_tmpl_t       src;
+       VALUE_PAIR      *vp, *head = NULL;
+       int             decoded = 0;
+       ssize_t         slen;
 
        while (isspace((int) *fmt)) fmt++;
 
-       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
-                *out = '\0';
-                return 0;
-       }
-
-       if ((fr_dhcp_decode_options(request->packet, &head, vp->vp_octets, vp->vp_length) < 0) || (!head)) {
-               RWDEBUG("DHCP option decoding failed: %s", fr_strerror());
+       slen = tmpl_from_attr_str(&src, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+       if (slen <= 0) {
+               REMARKER(fmt, slen, fr_strerror());
+       error:
                *out = '\0';
                return -1;
        }
 
+       if (src.type != TMPL_TYPE_ATTR) {
+               REDEBUG("dhcp_options cannot operate on a %s", fr_int2str(tmpl_names, src.type, "<INVALID>"));
+               goto error;
+       }
 
-       for (vp = fr_cursor_init(&cursor, &head);
-            vp;
-            vp = fr_cursor_next(&cursor)) {
-               decoded++;
+       if (src.tmpl_da->type != PW_TYPE_OCTETS) {
+               REDEBUG("dhcp_options got a %s attribute needed octets",
+                       fr_int2str(dict_attr_types, src.tmpl_da->type, "<INVALID>"));
+               goto error;
        }
 
-       pairmove(request->packet, &(request->packet->vps), &head);
+       for (vp = tmpl_cursor_init(NULL, &src_cursor, request, &src);
+            vp;
+            vp = tmpl_cursor_next(&src_cursor, &src)) {
+               /*
+                *      @fixme: we should pass in a cursor, then decoding multiple
+                *      source attributes can be made atomic.
+                */
+               if ((fr_dhcp_decode_options(request->packet, &head, vp->vp_octets, vp->vp_length) < 0) || (!head)) {
+                       RWDEBUG("DHCP option decoding failed: %s", fr_strerror());
+                       goto error;
+               }
+
+               for (vp = fr_cursor_init(&cursor, &head);
+                    vp;
+                    vp = fr_cursor_next(&cursor)) {
+                       rdebug_pair(L_DBG_LVL_2, request, vp, "dhcp_options: ");
+                       decoded++;
+               }
+
+               fr_pair_list_move(request->packet, &(request->packet->vps), &head);
 
-       /* Free any unmoved pairs */
-       pairfree(&head);
+               /* Free any unmoved pairs */
+               fr_pair_list_free(&head);
+       }
 
        snprintf(out, freespace, "%i", decoded);
 
@@ -122,7 +145,7 @@ static ssize_t dhcp_xlat(UNUSED void *instance, REQUEST *request, char const *fm
 /*
  *     Instantiate the module.
  */
-static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(UNUSED CONF_SECTION *conf, void *instance)
 {
        rlm_dhcp_t *inst = instance;
        DICT_ATTR const *da;
@@ -169,21 +192,8 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
  */
 extern module_t rlm_dhcp;
 module_t rlm_dhcp = {
-       RLM_MODULE_INIT,
-       "dhcp",
-       0,                              /* type */
-       sizeof(rlm_dhcp_t),
-       NULL,                           /* CONF_PARSER */
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL,                   /* post-auth */
-       },
+       .magic          = RLM_MODULE_INIT,
+       .name           = "dhcp",
+       .inst_size      = sizeof(rlm_dhcp_t),
+       .bootstrap      = mod_bootstrap,
 };
index 79d704d..9cd33c4 100644 (file)
@@ -1,2 +1,4 @@
 TARGET         := rlm_dhcp.a
-SOURCES                := rlm_dhcp.c ${top_srcdir}/src/modules/proto_dhcp/dhcp.c
+SOURCES                := rlm_dhcp.c
+
+TGT_PREREQS    := libfreeradius-dhcp.a
index c12723a..f1df128 100644 (file)
@@ -111,11 +111,13 @@ static int vqp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
 
 extern fr_protocol_t proto_vmps;
 fr_protocol_t proto_vmps = {
-       RLM_MODULE_INIT,
-       "vmps",
-       sizeof(listen_socket_t),
-       NULL,
-       common_socket_parse, NULL,
-       vqp_socket_recv, vqp_socket_send,
-       common_socket_print, vqp_socket_encode, vqp_socket_decode
+       .magic          = RLM_MODULE_INIT,
+       .name           = "vmps",
+       .inst_size      = sizeof(listen_socket_t),
+       .parse          = common_socket_parse,
+       .recv           = vqp_socket_recv,
+       .send           = vqp_socket_send,
+       .print          = common_socket_print,
+       .encode         = vqp_socket_encode,
+       .decode         = vqp_socket_decode
 };
index ef729fd..2f0521e 100644 (file)
@@ -5,8 +5,7 @@
  *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
- *   the Free Software Foundation; either version 2 of the License, or (at
- *   your option) any later version. either
+ *   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,
@@ -31,7 +30,7 @@ RCSID("$Id$")
 #define MAX_VMPS_LEN (MAX_STRING_LEN - 1)
 
 /* @todo: this is a hack */
-#  define debug_pair(vp)       do { if (fr_debug_flag && fr_log_fp) { \
+#  define debug_pair(vp)       do { if (fr_debug_lvl && fr_log_fp) { \
                                        vp_print(fr_log_fp, vp); \
                                     } \
                                } while(0)
@@ -423,7 +422,7 @@ int vqp_decode(RADIUS_PACKET *packet)
        if (packet->data_len < VQP_HDR_LEN) return -1;
 
        fr_cursor_init(&cursor, &packet->vps);
-       vp = paircreate(packet, PW_VQP_PACKET_TYPE, 0);
+       vp = fr_pair_afrom_num(packet, PW_VQP_PACKET_TYPE, 0);
        if (!vp) {
                fr_strerror_printf("No memory");
                return -1;
@@ -432,7 +431,7 @@ int vqp_decode(RADIUS_PACKET *packet)
        debug_pair(vp);
        fr_cursor_insert(&cursor, vp);
 
-       vp = paircreate(packet, PW_VQP_ERROR_CODE, 0);
+       vp = fr_pair_afrom_num(packet, PW_VQP_ERROR_CODE, 0);
        if (!vp) {
                fr_strerror_printf("No memory");
                return -1;
@@ -441,7 +440,7 @@ int vqp_decode(RADIUS_PACKET *packet)
        debug_pair(vp);
        fr_cursor_insert(&cursor, vp);
 
-       vp = paircreate(packet, PW_VQP_SEQUENCE_NUMBER, 0);
+       vp = fr_pair_afrom_num(packet, PW_VQP_SEQUENCE_NUMBER, 0);
        if (!vp) {
                fr_strerror_printf("No memory");
                return -1;
@@ -469,15 +468,22 @@ int vqp_decode(RADIUS_PACKET *packet)
                 *      Hack to get the dictionaries to work correctly.
                 */
                attribute |= 0x2000;
-               vp = paircreate(packet, attribute, 0);
+               vp = fr_pair_afrom_num(packet, attribute, 0);
                if (!vp) {
-                       pairfree(&packet->vps);
+                       fr_pair_list_free(&packet->vps);
 
                        fr_strerror_printf("No memory");
                        return -1;
                }
 
                switch (vp->da->type) {
+               case PW_TYPE_ETHERNET:
+                       if (length != 6) goto unknown;
+
+                       memcpy(&vp->vp_ether, ptr, 6);
+                       vp->vp_length = 6;
+                       break;
+
                case PW_TYPE_IPV4_ADDR:
                        if (length == 4) {
                                memcpy(&vp->vp_ipaddr, ptr, 4);
@@ -490,15 +496,16 @@ int vqp_decode(RADIUS_PACKET *packet)
                         *      valuepair so we must change it's da to an
                         *      unknown attr.
                         */
+               unknown:
                        vp->da = dict_unknown_afrom_fields(vp, vp->da->attr, vp->da->vendor);
                        /* FALL-THROUGH */
 
                default:
                case PW_TYPE_OCTETS:
                        if (length < 1024) {
-                               pairmemcpy(vp, ptr, length);
+                               fr_pair_value_memcpy(vp, ptr, length);
                        } else {
-                               pairmemcpy(vp, ptr, 1024);
+                               fr_pair_value_memcpy(vp, ptr, 1024);
                        }
                        break;
 
@@ -561,7 +568,7 @@ int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
 
        if (packet->data) return 0;
 
-       vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_VQP_PACKET_TYPE, 0, TAG_ANY);
        if (!vp) {
                fr_strerror_printf("Failed to find VQP-Packet-Type in response packet");
                return -1;
@@ -576,7 +583,7 @@ int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        length = VQP_HDR_LEN;
        memset(vps, 0, sizeof(vps));
 
-       vp = pairfind(packet->vps, PW_VQP_ERROR_CODE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_VQP_ERROR_CODE, 0, TAG_ANY);
 
        /*
         *      FIXME: Map attributes from calling-station-Id, etc.
@@ -591,7 +598,7 @@ int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) {
                if (!contents[code][i]) break;
 
-               vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000, 0, TAG_ANY);
+               vps[i] = fr_pair_find_by_num(packet->vps, contents[code][i] | 0x2000, 0, TAG_ANY);
 
                /*
                 *      FIXME: Print the name...
@@ -684,6 +691,10 @@ int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                        memcpy(ptr, &vp->vp_ipaddr, 4);
                        break;
 
+               case PW_TYPE_ETHERNET:
+                       memcpy(ptr, vp->vp_ether, vp->vp_length);
+                       break;
+
                default:
                case PW_TYPE_OCTETS:
                case PW_TYPE_STRING:
diff --git a/src/modules/proto_vmps/vqpcli.pl b/src/modules/proto_vmps/vqpcli.pl
new file mode 100755 (executable)
index 0000000..a877046
--- /dev/null
@@ -0,0 +1,207 @@
+#!/usr/bin/env perl
+
+#   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
+
+#
+# vqpcli.pl -s localhost -v mydomain -w 10.0.0.1 -i 2/4 -m 0010.a49f.30e3
+#
+
+use Socket;
+$|=0;
+#$DEBUG=1;
+$DEBUG=0;
+
+sub formatItem($$) {
+
+       my $mybuf;
+       undef($mybuf);
+
+       $itemheader = shift;
+       $itemvalue = shift;
+
+       $mybuf = $mybuf . pack("H*",(unpack("a*",$itemheader))); # Add header 
+
+       $payload = pack("a*",(unpack("a*",$itemvalue)));
+       $length=length($payload);
+       $length= pack("H*",(unpack("a*",sprintf("%04x",$length))));
+
+       $mybuf = $mybuf . $length . $payload; # Add payload + length
+
+       return $mybuf;
+}
+
+sub parseOpts() {
+       use Getopt::Std;
+       my $errors = "";
+       
+       getopts("s:v:w:i:m:t:c:",\%opt) or usage();
+       usage() if $opt{h};
+       my %request = (
+               server_ip       =>      $opt{s} || "",
+               client_ip       =>      $opt{w} || "127.0.0.1", # IP to say we are - VMPS doesn't care
+               port_name       =>      $opt{i} || "Fa0/1", # Default port name to use
+               vlan            =>      $opt{c} || "", # Isn't really needed. 
+               vtp_domain      =>      $opt{v} || "", # Is kinda important
+               macaddr         =>      $opt{m} || "", # Likewise...
+       );
+
+       $opt{m} =~ tr/A-Z/a-z/;
+       $errors=$errors . "MAC address must be in nnnn.nnnn.nnnn format\n" 
+               if ($opt{m} !~ /[a-z0-9][a-z0-9][a-z0-9][a-z0-9]\.[a-z0-9][a-z0-9][a-z0-9][a-z0-9]\.[a-z0-9][a-z0-9][a-z0-9][a-z0-9]/);
+       $errors=$errors . "VTP Domain must be specified\n" if ($opt{v} !~ /.*/);
+       $errors=$errors . "No Server name specified\n" if ($opt{s} =~ /^$/);
+       print STDERR $errors if ($errors);
+       usage() if ($errors);
+       $request{macaddr} =~ s/\.//g;
+       
+       return %request;
+}
+
+sub usage() {
+        print STDERR << "EOO";
+Options:
+-s ip      VMPS Server to query
+-v domain  VMPS/VTP Domain to query
+-w ip      client switch IP to query for
+-i iface   client switch Interface name (ie: Fa0/17)
+-m macaddr attached device MAC address in nnnn.nnnn.nnnn format
+-c vlan    Vlan to reconfirm membership to
+
+EOO
+       exit(1);
+
+}
+
+sub makeVQPrequest($) {
+
+       my $request = $_;
+       my $buf;
+
+       # Header...
+       $buf = $buf . pack("H*",(unpack("a*","01"))); # Header bit
+
+       # Is a request to join a vlan
+       $buf = $buf . pack("H*",(unpack("a*","01"))); # Is a request
+
+       # No error
+       $buf = $buf . pack("H*",(unpack("a*","00"))); # No error
+
+       # 6 data items in inbound payload
+       $buf = $buf . pack("H*",(unpack("a*","06")));
+
+       # Sequence number of request
+       $buf = $buf . pack("H*",(unpack("a*","000 1234"))); # Bogus sequence number
+
+       # Add Client switch IP
+       $buf = $buf . formatItem("000 0c01",(sprintf("%s",unpack("a*",inet_aton($request{client_ip})))));
+
+       # Add Port Name
+       $buf = $buf . formatItem("000 0c02",$request{port_name}); # Payload 
+
+       # Add VLAN to confirm to buffer
+       $buf = $buf . formatItem("000 0c03",$request{vlan}); # Payload 
+
+       # Add VTP domain name
+       $buf = $buf . formatItem("000 0c04",$request{vtp_domain}); # Payload 
+
+       # Add UNKNOWN data to buffer...
+       $buf = $buf . pack("H*",(unpack("a*","000 0c07"))); # Header
+       $buf = $buf . pack("H*",(unpack("a*","0001 0"))); # Unknown filler
+
+       # Add MAC address to buffer
+       $buf = $buf . formatItem("000 0c06",sprintf("%s",pack("H*",(unpack("a*",$request{macaddr}))))); # Payload 
+
+       return "$buf";
+}
+
+sub sendVQP($$) {
+
+       my $PORTNO="1589";
+       my $HOSTNAME= shift;
+       my $buf = shift;
+
+       if ($DEBUG==1) {
+               print "==============================\n";
+               print "MESSAGE SENT:\n";
+               open (HEX, "|/usr/bin/hexdump");
+               select HEX;
+               print $buf;
+               close HEX;
+               select STDOUT;
+               print "==============================\n";
+       }
+
+       socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die "socket: $!";
+
+       my $ipaddr   = inet_aton($HOSTNAME);
+       my $portaddr = sockaddr_in($PORTNO, $ipaddr);
+       send(SOCKET, $buf, 0, $portaddr) == length($buf)
+               or die "cannot send to $HOSTNAME($PORTNO): $!";
+
+       $portaddr = recv(SOCKET, $buf, 1500, 0); # or die "recv: $!";
+
+       if ($DEBUG==1) {
+               print "MESSAGE RECV:\n";
+               open (HEX, "|/usr/bin/hexdump");
+               select HEX;
+               print $buf;
+               close HEX;
+               select STDOUT;
+               print "==============================\n";
+       }
+       return "$buf";
+}
+
+sub parseVQPresp($) {
+
+       my %response = (
+               status          =>      "",
+               vlan            =>      "",
+               macaddr         =>      "",
+       );
+
+       my $buf = shift;
+       $buf =~ /^(.)(.)(.)(.)(....)/;
+       my ($header,$type,$status,$size,$sequence) = 
+               (ord($1),ord($2),ord($3),ord($4),pack("a*",(unpack("H*",$5))));
+
+       $buf =~ s/^........//;
+
+       $response{status}="ALLOW" if ($status == 0);
+       $response{status}="DENY" if ($status == 3);
+       $response{status}="SHUTDOWN" if ($status == 4);
+       $response{status}="WRONG_DOMAIN" if ($status == 5);
+
+       for ($i=1;$i<=$size;$i++) {
+
+               $payload_type=pack("a*",(unpack("H*",substr($buf,0,4))));
+               $payload_size=sprintf("%d",hex(pack("a*",(unpack("H*",substr($buf,4,2))))));
+               $payload=substr($buf,6,$payload_size);
+
+               if ($payload_type eq "00000c03") {
+                       $response{vlan}=$payload;
+               } elsif ($payload_type eq"00000c08") {
+                       $response{macaddr}=pack("a*",(unpack("H*",$payload)));
+               }
+               substr($buf,0,($payload_size + 6)) = "";
+       }
+       return %response;
+}
+
+%request=parseOpts();
+$buf = makeVQPrequest(%request);
+$buf = sendVQP($request{server_ip},$buf);
+%response = parseVQPresp($buf);
+print "Vlan: $response{vlan}\nMAC Address: $response{macaddr} \nStatus: $response{status}\n";
index 07e0fe2..6a75164 100644 (file)
@@ -49,8 +49,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
@@ -126,30 +125,26 @@ static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST *requ
 
 extern module_t rlm_always;
 module_t rlm_always = {
-       RLM_MODULE_INIT,
-       "always",
-       RLM_TYPE_HUP_SAFE,              /* needed for radmin */
-       sizeof(rlm_always_t),           /* config size */
-       module_config,                  /* configuration */
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_always_return,              /* authentication */
-               mod_always_return,              /* authorization */
-               mod_always_return,              /* preaccounting */
-               mod_always_return,              /* accounting */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "always",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_always_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_always_return,
+               [MOD_AUTHORIZE]         = mod_always_return,
+               [MOD_PREACCT]           = mod_always_return,
+               [MOD_ACCOUNTING]        = mod_always_return,
 #ifdef WITH_SESSION_MGMT
-               mod_checksimul, /* checksimul */
-#else
-               NULL,
+               [MOD_SESSION]           = mod_checksimul,
 #endif
-               mod_always_return,              /* pre-proxy */
-               mod_always_return,              /* post-proxy */
-               mod_always_return               /* post-auth */
+               [MOD_PRE_PROXY]         = mod_always_return,
+               [MOD_POST_PROXY]        = mod_always_return,
+               [MOD_POST_AUTH]         = mod_always_return,
 #ifdef WITH_COA
-               ,
-               mod_always_return,              /* recv-coa */
-               mod_always_return               /* send-coa */
+               [MOD_RECV_COA]          = mod_always_return,
+               [MOD_SEND_COA]          = mod_always_return
 #endif
        },
 };
index dd8ff90..a2ffe6f 100644 (file)
@@ -49,7 +49,7 @@ static const CONF_PARSER module_config[] = {
        { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, rlm_attr_filter_t, filename), NULL },
        { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_attr_filter_t, key), "%{Realm}" },
        { "relaxed", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_attr_filter_t, relaxed), "no" },
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 static void check_pair(REQUEST *request, VALUE_PAIR *check_item, VALUE_PAIR *reply_item, int *pass, int *fail)
@@ -58,7 +58,7 @@ static void check_pair(REQUEST *request, VALUE_PAIR *check_item, VALUE_PAIR *rep
 
        if (check_item->op == T_OP_SET) return;
 
-       compare = paircmp(check_item, reply_item);
+       compare = fr_pair_cmp(check_item, reply_item);
        if (compare < 0) {
                REDEBUG("Comparison failed: %s", fr_strerror());
        }
@@ -165,7 +165,7 @@ static rlm_rcode_t CC_HINT(nonnull(1,2)) attr_filter_common(void *instance, REQU
        if (!inst->key) {
                VALUE_PAIR      *namepair;
 
-               namepair = pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY);
+               namepair = fr_pair_find_by_num(request->packet->vps, PW_REALM, 0, TAG_ANY);
                if (!namepair) {
                        return (RLM_MODULE_NOOP);
                }
@@ -228,7 +228,7 @@ static rlm_rcode_t CC_HINT(nonnull(1,2)) attr_filter_common(void *instance, REQU
                         *    the output list without checking it.
                         */
                        if (check_item->op == T_OP_SET ) {
-                               vp = paircopyvp(packet, check_item);
+                               vp = fr_pair_copy(packet, check_item);
                                if (!vp) {
                                        goto error;
                                }
@@ -281,7 +281,7 @@ static rlm_rcode_t CC_HINT(nonnull(1,2)) attr_filter_common(void *instance, REQU
                                if (!pass) {
                                        RDEBUG3("Attribute \"%s\" allowed by relaxed mode", input_item->da->name);
                                }
-                               vp = paircopyvp(packet, input_item);
+                               vp = fr_pair_copy(packet, input_item);
                                if (!vp) {
                                        goto error;
                                }
@@ -306,21 +306,21 @@ static rlm_rcode_t CC_HINT(nonnull(1,2)) attr_filter_common(void *instance, REQU
        /*
         *      Replace the existing request list with our filtered one
         */
-       pairfree(&packet->vps);
+       fr_pair_list_free(&packet->vps);
        packet->vps = output;
 
        if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
-               request->username = pairfind(request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
+               request->username = fr_pair_find_by_num(request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
                if (!request->username) {
-                       request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+                       request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                }
-               request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+               request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
        }
 
        return RLM_MODULE_UPDATED;
 
        error:
-       pairfree(&output);
+       fr_pair_list_free(&output);
        return RLM_MODULE_FAIL;
 }
 
@@ -348,30 +348,24 @@ RLM_AF_FUNC(send_coa, reply)
 /* globally exported name */
 extern module_t rlm_attr_filter;
 module_t rlm_attr_filter = {
-       RLM_MODULE_INIT,
-       "attr_filter",
-       RLM_TYPE_HUP_SAFE,      /* type */
-       sizeof(rlm_attr_filter_t),
-       module_config,
-       mod_instantiate,        /* instantiation */
-       NULL,                   /* detach */
-       {
-               NULL,           /* authentication */
-               mod_authorize,  /* authorization */
-               mod_preacct,    /* pre-acct */
-               mod_accounting, /* accounting */
-               NULL,           /* checksimul */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "attr_filter",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_attr_filter_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preacct,
+               [MOD_ACCOUNTING]        = mod_accounting,
 #ifdef WITH_PROXY
-               mod_pre_proxy,  /* pre-proxy */
-               mod_post_proxy, /* post-proxy */
-#else
-               NULL, NULL,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
+               [MOD_POST_PROXY]        = mod_post_proxy,
 #endif
-               mod_post_auth   /* post-auth */
+               [MOD_POST_AUTH]         = mod_post_auth,
 #ifdef WITH_COA
-               ,
-               mod_recv_coa,
-               mod_send_coa
+               [MOD_RECV_COA]          = mod_recv_coa,
+               [MOD_SEND_COA]          = mod_send_coa
 #endif
        },
 };
index 72eede2..93e3b08 100755 (executable)
@@ -3117,6 +3117,181 @@ $as_echo "$as_me: WARNING: libmemcached headers not found. Use --with-libmemcach
        fi
 
 
+
+
+sm_lib_safe=`echo "pthread" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "pthread_once" | 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 pthread_once in -lpthread in $try" >&5
+$as_echo_n "checking for pthread_once in -lpthread in $try... " >&6; }
+    LIBS="-lpthread $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char pthread_once();
+int
+main ()
+{
+pthread_once()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-lpthread"
+                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 pthread_once in -lpthread" >&5
+$as_echo_n "checking for pthread_once in -lpthread... " >&6; }
+  LIBS="-lpthread $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char pthread_once();
+int
+main ()
+{
+pthread_once()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-lpthread"
+               { $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=libpthread${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=libpthread.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 pthread_once in -lpthread in $try" >&5
+$as_echo_n "checking for pthread_once in -lpthread in $try... " >&6; }
+    LIBS="-lpthread $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char pthread_once();
+int
+main ()
+{
+pthread_once()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-lpthread"
+                 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
+
+
        smart_try_dir="$libmemcached_lib_dir"
 
 
index dcb1bf0..5fe56bd 100644 (file)
@@ -81,6 +81,10 @@ if test x$with_[]modname != xno; then
        dnl # Check for libmemcached libraries
        dnl ############################################################
 
+    dnl # Check if libpthread is available. Should add -lpthread
+    dnl # to CFLAGS when checking for memcached.
+    FR_SMART_CHECK_LIB([pthread], [pthread_once])
+
        smart_try_dir="$libmemcached_lib_dir"
        dnl # Use a libmemcached specific function which is only
        dnl # available in newer versions.
index 7e72f60..a8161cb 100644 (file)
@@ -40,8 +40,7 @@ typedef struct rlm_cache_memcached {
 
 static const CONF_PARSER driver_config[] = {
        { "options", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_cache_memcached_t, options), "--SERVER=localhost" },
-
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 /** Free a connection handle
@@ -98,7 +97,7 @@ static void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
  */
 static int _mod_detach(rlm_cache_memcached_t *driver)
 {
-       fr_connection_pool_delete(driver->pool);
+       fr_connection_pool_free(driver->pool);
        return 0;
 }
 
@@ -142,7 +141,7 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_cache_t *inst)
 
        inst->driver = driver;
 
-       snprintf(buffer, sizeof(buffer), "rlm_cache (%s)", inst->xlat_name);
+       snprintf(buffer, sizeof(buffer), "rlm_cache (%s)", inst->name);
 
        driver->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, buffer);
        if (!driver->pool) return -1;
@@ -334,16 +333,15 @@ static int mod_conn_reconnect(rlm_cache_t *inst, UNUSED REQUEST *request, rlm_ca
 
 extern cache_module_t rlm_cache_memcached;
 cache_module_t rlm_cache_memcached = {
-       "rlm_cache_memcached",
-       mod_instantiate,
-       NULL,                   /* alloc */
-       cache_entry_free,
-       cache_entry_find,
-       cache_entry_insert,
-       cache_entry_expire,
-       NULL,                   /* count */
-
-       mod_conn_get,
-       mod_conn_release,
-       mod_conn_reconnect
+       .name           = "rlm_cache_memcached",
+       .instantiate    = mod_instantiate,
+       .free           = cache_entry_free,
+
+       .find           = cache_entry_find,
+       .insert         = cache_entry_insert,
+       .expire         = cache_entry_expire,
+
+       .acquire        = mod_conn_get,
+       .release        = mod_conn_release,
+       .reconnect      = mod_conn_reconnect
 };
index c79f1b0..2db7c93 100644 (file)
@@ -337,16 +337,15 @@ static void cache_release(UNUSED rlm_cache_t *inst, REQUEST *request, rlm_cache_
 
 extern cache_module_t rlm_cache_rbtree;
 cache_module_t rlm_cache_rbtree = {
-       "rlm_cache_rbtree",
-       mod_instantiate,
-       cache_entry_alloc,
-       NULL,                   /* free */
-       cache_entry_find,
-       cache_entry_insert,
-       cache_entry_expire,
-       cache_entry_count,
-
-       cache_acquire,
-       cache_release,
-       NULL                    /* no reconnect method */
+       .name           = "rlm_cache_rbtree",
+       .instantiate    = mod_instantiate,
+       .alloc          = cache_entry_alloc,
+
+       .find           = cache_entry_find,
+       .insert         = cache_entry_insert,
+       .expire         = cache_entry_expire,
+       .count          = cache_entry_count,
+
+       .acquire        = cache_acquire,
+       .release        = cache_release,
 };
index 69466e7..e9572bc 100644 (file)
@@ -49,8 +49,7 @@ static const CONF_PARSER module_config[] = {
        /* 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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static int cache_acquire(rlm_cache_handle_t **out, rlm_cache_t *inst, REQUEST *request)
@@ -119,7 +118,7 @@ static void CC_HINT(nonnull) cache_merge(rlm_cache_t *inst, REQUEST *request, rl
 {
        VALUE_PAIR *vp;
 
-       vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_CACHE_MERGE, 0, TAG_ANY);
        if (vp && (vp->vp_integer == 0)) {
                RDEBUG2("Told not to merge entry into request");
                return;
@@ -127,28 +126,33 @@ static void CC_HINT(nonnull) cache_merge(rlm_cache_t *inst, REQUEST *request, rl
 
        RDEBUG2("Merging cache entry into request");
 
-       if (c->control) {
-               rdebug_pair_list(L_DBG_LVL_2, request, c->control, "&control:");
-               radius_pairmove(request, &request->config_items, paircopy(request, c->control), false);
-       }
-
        if (c->packet && request->packet) {
                rdebug_pair_list(L_DBG_LVL_2, request, c->packet, "&request:");
-               radius_pairmove(request, &request->packet->vps, paircopy(request->packet, c->packet), false);
+               radius_pairmove(request, &request->packet->vps, fr_pair_list_copy(request->packet, c->packet), false);
        }
 
        if (c->reply && request->reply) {
                rdebug_pair_list(L_DBG_LVL_2, request, c->reply, "&reply:");
-               radius_pairmove(request, &request->reply->vps, paircopy(request->reply, c->reply), false);
+               radius_pairmove(request, &request->reply->vps, fr_pair_list_copy(request->reply, c->reply), false);
+       }
+
+       if (c->control) {
+               rdebug_pair_list(L_DBG_LVL_2, request, c->control, "&control:");
+               radius_pairmove(request, &request->config, fr_pair_list_copy(request, c->control), false);
+       }
+
+       if (c->state) {
+               rdebug_pair_list(L_DBG_LVL_2, request, c->state, "&session-state:");
+               radius_pairmove(request, &request->state, fr_pair_list_copy(request->state, c->state), false);
        }
 
        if (inst->stats) {
                rad_assert(request->packet != NULL);
-               vp = pairfind(request->packet->vps, PW_CACHE_ENTRY_HITS, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_CACHE_ENTRY_HITS, 0, TAG_ANY);
                if (!vp) {
-                       vp = paircreate(request->packet, PW_CACHE_ENTRY_HITS, 0);
+                       vp = fr_pair_afrom_num(request->packet, PW_CACHE_ENTRY_HITS, 0);
                        rad_assert(vp != NULL);
-                       pairadd(&request->packet->vps, vp);
+                       fr_pair_add(&request->packet->vps, vp);
                }
                vp->vp_integer = c->hits;
        }
@@ -239,9 +243,9 @@ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_h
                                char const *key, int ttl)
 {
        VALUE_PAIR *vp, *to_cache;
-       vp_cursor_t src_list, cached_request, cached_reply, cached_control;
+       vp_cursor_t src_list, packet, reply, control, state;
 
-       value_pair_map_t const *map;
+       vp_map_t const *map;
 
        bool merge = true;
        rlm_cache_entry_t *c;
@@ -261,21 +265,19 @@ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_h
 
        RDEBUG("Creating new cache entry");
 
-       fr_cursor_init(&cached_request, &c->packet);
-       fr_cursor_init(&cached_reply, &c->reply);
-       fr_cursor_init(&cached_control, &c->control);
+       fr_cursor_init(&packet, &c->packet);
+       fr_cursor_init(&reply, &c->reply);
+       fr_cursor_init(&control, &c->control);
+       fr_cursor_init(&state, &c->state);
 
        for (map = inst->maps; map != NULL; map = map->next) {
                rad_assert(map->lhs && map->rhs);
 
-               if (map_to_vp(&to_cache, request, map, NULL) < 0) {
+               if (map_to_vp(c, &to_cache, request, map, NULL) < 0) {
                        RDEBUG("Skipping %s", map->rhs->name);
                        continue;
                }
 
-               /*
-                *      Reparent the VPs map_to_vp may return multiple.
-                */
                for (vp = fr_cursor_init(&src_list, &to_cache);
                     vp;
                     vp = fr_cursor_next(&src_list)) {
@@ -301,21 +303,24 @@ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_h
                        RINDENT();
                        if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
                        REXDENT();
-                       (void) talloc_steal(c, vp);
 
                        vp->op = map->op;
 
                        switch (map->lhs->tmpl_list) {
                        case PAIR_LIST_REQUEST:
-                               fr_cursor_insert(&cached_request, vp);
+                               fr_cursor_insert(&packet, vp);
                                break;
 
                        case PAIR_LIST_REPLY:
-                               fr_cursor_insert(&cached_reply, vp);
+                               fr_cursor_insert(&reply, vp);
                                break;
 
                        case PAIR_LIST_CONTROL:
-                               fr_cursor_insert(&cached_control, vp);
+                               fr_cursor_insert(&control, vp);
+                               break;
+
+                       case PAIR_LIST_STATE:
+                               fr_cursor_insert(&state, vp);
                                break;
 
                        default:
@@ -327,7 +332,7 @@ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_h
        /*
         *      Check to see if we need to merge the entry into the request
         */
-       vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_CACHE_MERGE, 0, TAG_ANY);
        if (vp && (vp->vp_integer == 0)) merge = false;
 
        if (merge) cache_merge(inst, request, c);
@@ -342,7 +347,7 @@ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_h
                        return RLM_MODULE_FAIL;
 
                case CACHE_OK:
-                       RDEBUG("Commited entry, TTL %d seconds", ttl);
+                       RDEBUG("Committed entry, TTL %d seconds", ttl);
                        cache_free(inst, &c);
                        return RLM_MODULE_UPDATED;
 
@@ -356,13 +361,30 @@ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_h
 /** Verify that a map in the cache section makes sense
  *
  */
-static int cache_verify(value_pair_map_t *map, void *ctx)
+static int cache_verify(vp_map_t *map, void *ctx)
 {
        if (modcall_fixup_update(map, ctx) < 0) return -1;
 
        if ((map->lhs->type != TMPL_TYPE_ATTR) &&
            (map->lhs->type != TMPL_TYPE_LIST)) {
-               cf_log_err(map->ci, "Left operand must be an attribute ref or a list");
+               cf_log_err(map->ci, "Destination must be an attribute ref or a list");
+               return -1;
+       }
+
+       switch (map->lhs->tmpl_list) {
+       case PAIR_LIST_REQUEST:
+       case PAIR_LIST_REPLY:
+       case PAIR_LIST_CONTROL:
+       case PAIR_LIST_STATE:
+               break;
+
+       default:
+               cf_log_err(map->ci, "Destination list must be one of request, reply, control or session-state");
+               return -1;
+       }
+
+       if (map->lhs->tmpl_request != REQUEST_CURRENT) {
+               cf_log_err(map->ci, "Cached attributes can only be inserted into the current request");
                return -1;
        }
 
@@ -435,7 +457,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(void *instance, REQUEST *reques
         *      If Cache-Status-Only == yes, only return whether we found a
         *      valid cache entry
         */
-       vp = pairfind(request->config_items, PW_CACHE_STATUS_ONLY, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_CACHE_STATUS_ONLY, 0, TAG_ANY);
        if (vp && vp->vp_integer) {
                rcode = c ? RLM_MODULE_OK:
                            RLM_MODULE_NOTFOUND;
@@ -447,7 +469,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(void *instance, REQUEST *reques
         *      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);
+       vp = fr_pair_find_by_num(request->config, PW_CACHE_TTL, 0, TAG_ANY);
        if (vp) ttl = vp->vp_signed;
 
        /*
@@ -493,7 +515,7 @@ insert:
         *      If Cache-Read-Only == yes, then we only allow already cached entries
         *      to be merged into the request
         */
-       vp = pairfind(request->config_items, PW_CACHE_READ_ONLY, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_CACHE_READ_ONLY, 0, TAG_ANY);
        if (vp && vp->vp_integer) {
                rcode = RLM_MODULE_NOTFOUND;
                goto finish;
@@ -512,7 +534,7 @@ finish:
        /*
         *      Clear control attributes
         */
-       for (vp = fr_cursor_init(&cursor, &request->config_items);
+       for (vp = fr_cursor_init(&cursor, &request->config);
             vp;
             vp = fr_cursor_next(&cursor)) {
                if (vp->da->vendor == 0) switch (vp->da->attr) {
@@ -589,13 +611,17 @@ static ssize_t cache_xlat(void *instance, REQUEST *request,
                vps = c->control;
                break;
 
+       case PAIR_LIST_STATE:
+               vps = c->state;
+               break;
+
        default:
                REDEBUG("Unsupported list \"%s\"", fr_int2str(pair_lists, list, "<UNKNOWN>"));
                ret = -1;
                goto finish;
        }
 
-       vp = pairfind(vps, target->attr, target->vendor, TAG_ANY);
+       vp = fr_pair_find_by_num(vps, target->attr, target->vendor, TAG_ANY);
        if (!vp) {
                RDEBUG("No instance of this attribute has been cached");
                *out = '\0';
@@ -645,29 +671,40 @@ static int mod_detach(void *instance)
        return 0;
 }
 
-/*
- *     Instantiate the module.
- */
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        rlm_cache_t *inst = instance;
-       CONF_SECTION *update;
 
        inst->cs = conf;
 
-       inst->xlat_name = cf_section_name2(conf);
-       if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
+       inst->name = cf_section_name2(conf);
+       if (!inst->name) inst->name = cf_section_name1(conf);
 
        /*
         *      Register the cache xlat function
         */
-       xlat_register(inst->xlat_name, cache_xlat, NULL, inst);
+       xlat_register(inst->name, cache_xlat, NULL, inst);
+
+       return 0;
+}
+
+
+/*
+ *     Instantiate the module.
+ */
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+       rlm_cache_t *inst = instance;
+       CONF_SECTION *update;
+
+       inst->cs = conf;
 
        /*
         *      Sanity check for crazy people.
         */
        if (strncmp(inst->driver_name, "rlm_cache_", 8) != 0) {
-               ERROR("rlm_cache (%s): \"%s\" is NOT an Cache driver!", inst->xlat_name, inst->driver_name);
+               cf_log_err_cs(conf, "\"%s\" is NOT an Cache driver!", inst->driver_name);
                return -1;
        }
 
@@ -676,20 +713,20 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
         */
        inst->handle = lt_dlopenext(inst->driver_name);
        if (!inst->handle) {
-               ERROR("rlm_cache (%s): Could not link driver %s: %s", inst->xlat_name, inst->driver_name, dlerror());
-               ERROR("rlm_cache (%s): Make sure it (and all its dependent libraries!) are in the search path"
-                     "of your system's ld", inst->xlat_name);
+               cf_log_err_cs(conf, "Could not link driver %s: %s", inst->driver_name, dlerror());
+               cf_log_err_cs(conf, "Make sure it (and all its dependent libraries!) are in the search path"
+                             " of your system's ld");
                return -1;
        }
 
        inst->module = (cache_module_t *) dlsym(inst->handle, inst->driver_name);
        if (!inst->module) {
-               ERROR("rlm_cache (%s): Could not link symbol %s: %s", inst->xlat_name, inst->driver_name, dlerror());
+               cf_log_err_cs(conf, "Could not link symbol %s: %s", inst->driver_name, dlerror());
                return -1;
        }
 
-       INFO("rlm_cache (%s): Driver %s (module %s) loaded and linked", inst->xlat_name,
-            inst->driver_name, inst->module->name);
+       DEBUG("rlm_cache (%s): Driver %s (module %s) loaded and linked", inst->name,
+             inst->driver_name, inst->module->name);
 
        /*
         *      Non optional fields and callbacks
@@ -699,7 +736,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        rad_assert(inst->module->insert);
        rad_assert(inst->module->expire);
 
-       if (inst->module->mod_instantiate) {
+       if (inst->module->instantiate) {
                CONF_SECTION *cs;
                char const *name;
 
@@ -722,7 +759,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                 *      Should write its instance data in inst->driver,
                 *      and parent it off of inst.
                 */
-               if (inst->module->mod_instantiate(cs, inst) < 0) return -1;
+               if (inst->module->instantiate(cs, inst) < 0) return -1;
        }
 
        rad_assert(inst->key && *inst->key);
@@ -771,21 +808,19 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  */
 extern module_t rlm_cache;
 module_t rlm_cache = {
-       RLM_MODULE_INIT,
-       "cache",
-       0,                              /* type */
-       sizeof(rlm_cache_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_cache_it,           /* authorization */
-               mod_cache_it,           /* preaccounting */
-               mod_cache_it,           /* accounting */
-               NULL,                   /* checksimul */
-               mod_cache_it,           /* pre-proxy */
-               mod_cache_it,           /* post-proxy */
-               mod_cache_it,           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "cache",
+       .inst_size      = sizeof(rlm_cache_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_cache_it,
+               [MOD_PREACCT]           = mod_cache_it,
+               [MOD_ACCOUNTING]        = mod_cache_it,
+               [MOD_PRE_PROXY]         = mod_cache_it,
+               [MOD_POST_PROXY]        = mod_cache_it,
+               [MOD_POST_AUTH]         = mod_cache_it
        },
 };
index acf8495..10f6a4c 100644 (file)
@@ -47,7 +47,7 @@ typedef enum {
  *     be used as the instance handle.
  */
 typedef struct rlm_cache_t {
-       char const              *xlat_name;             //!< Name of xlat function to register.
+       char const              *name;          //!< Name of xlat function to register.
 
        char const              *driver_name;           //!< Datastore name
        void                    *handle;                //!< Datastore handle.
@@ -60,7 +60,7 @@ typedef struct rlm_cache_t {
        int32_t                 epoch;                  //!< Time after which entries are considered valid.
        bool                    stats;                  //!< Generate statistics.
 
-       value_pair_map_t        *maps;                  //!< Attribute map applied to users.
+       vp_map_t        *maps;                  //!< Attribute map applied to users.
                                                        //!< and profiles.
        CONF_SECTION            *cs;
 } rlm_cache_t;
@@ -74,9 +74,10 @@ typedef struct rlm_cache_entry_t {
        VALUE_PAIR              *control;               //!< Cached control list.
        VALUE_PAIR              *packet;                //!< Cached request list.
        VALUE_PAIR              *reply;                 //!< Cached reply list.
+       VALUE_PAIR              *state;                 //!< Cached session-state list.
 } rlm_cache_entry_t;
 
-typedef int                    (*mod_instantiate_t)(CONF_SECTION *conf, rlm_cache_t *inst);
+typedef int                    (*cache_instantiate_t)(CONF_SECTION *conf, rlm_cache_t *inst);
 typedef rlm_cache_entry_t      *(*cache_entry_alloc_t)(rlm_cache_t *inst, REQUEST *request);
 typedef void                   (*cache_entry_free_t)(rlm_cache_entry_t *c);
 
@@ -96,7 +97,7 @@ typedef int                   (*cache_reconnect_t)(rlm_cache_t *inst, REQUEST *request, rlm_cach
 struct cache_module {
        char const              *name;                  //!< Driver name.
 
-       mod_instantiate_t       mod_instantiate;        //!< (optional) Instantiate a driver.
+       cache_instantiate_t     instantiate;            //!< (optional) Instantiate a driver.
        cache_entry_alloc_t     alloc;                  //!< (optional) Allocate a new entry.
        cache_entry_free_t      free;                   //!< (optional) Free memory used by an entry.
 
index ad917fc..b1f8e0c 100644 (file)
@@ -104,6 +104,18 @@ int cache_serialize(TALLOC_CTX *ctx, char **out, rlm_cache_entry_t *c)
                }
        }
 
+       if (c->state) {
+               for (vp = fr_cursor_init(&cursor, &c->state);
+                    vp;
+                    vp = fr_cursor_next(&cursor)) {
+                       pair = vp_aprints(pairs, vp, '\'');
+                       if (!pair) goto error;
+
+                       to_store = talloc_asprintf_append_buffer(to_store, "&session-state:%s\n", pair);
+                       if (!to_store) goto error;
+               }
+       }
+
 finish:
        talloc_free(pairs);
        *out = to_store;
@@ -121,7 +133,7 @@ finish:
  */
 int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen)
 {
-       vp_cursor_t packet, control, reply;
+       vp_cursor_t packet, control, reply, state;
 
        TALLOC_CTX *store = NULL;
        char *p, *q;
@@ -134,11 +146,12 @@ int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen)
        fr_cursor_init(&packet, &c->packet);
        fr_cursor_init(&control, &c->control);
        fr_cursor_init(&reply, &c->reply);
+       fr_cursor_init(&state, &c->state);
 
        p = in;
 
        while (((size_t)(p - in)) < (size_t)inlen) {
-               value_pair_map_t *map = NULL;
+               vp_map_t *map = NULL;
                VALUE_PAIR *vp = NULL;
                ssize_t len;
 
@@ -168,15 +181,15 @@ int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen)
                }
 
                /*
-                *      Convert literal to a type appropriate for
-                *      the VP.
+                *      Convert literal to a type appropriate for the VP.
                 */
-               if (!tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da)) goto error;
+               if (tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) goto error;
 
-               vp = pairalloc(c, map->lhs->tmpl_da);
+               vp = fr_pair_afrom_da(c, map->lhs->tmpl_da);
                len = value_data_copy(vp, &vp->data, map->rhs->tmpl_data_type,
                                      &map->rhs->tmpl_data_value, map->rhs->tmpl_data_length);
                if (len < 0) goto error;
+               vp->vp_length = len;
 
                /*
                 *      Pull out the special attributes, and set the
@@ -210,6 +223,10 @@ int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen)
                        fr_cursor_insert(&reply, vp);
                        break;
 
+               case PAIR_LIST_STATE:
+                       fr_cursor_insert(&state, vp);
+                       break;
+
                default:
                        fr_strerror_printf("Invalid cache list for pair: %s", p);
                error:
index f62469b..4f79fe3 100644 (file)
@@ -29,17 +29,19 @@ RCSID("$Id$")
 
 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
 {
-       if (!pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
+       if (!fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
                return RLM_MODULE_NOOP;
        }
 
-       if (pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY) != NULL) {
-               RWDEBUG2("Auth-Type already set.  Not setting to CHAP");
+       if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY) != NULL) {
+               RWDEBUG2("&control:Auth-Type already set.  Not setting to CHAP");
                return RLM_MODULE_NOOP;
        }
 
-       RDEBUG("Setting 'Auth-Type := CHAP'");
-       pairmake_config("Auth-Type", "CHAP", T_OP_EQ);
+       RINDENT();
+       RDEBUG("&control:Auth-Type := CHAP");
+       REXDENT();
+       pair_make_config("Auth-Type", "CHAP", T_OP_EQ);
 
        return RLM_MODULE_OK;
 }
@@ -53,38 +55,34 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request)
 {
-       VALUE_PAIR *passwd_item, *chap;
+       VALUE_PAIR *password, *chap;
        uint8_t pass_str[MAX_STRING_LEN];
 
        if (!request->username) {
-               RWDEBUG("Attribute 'User-Name' is required for authentication.\n");
+               RWDEBUG("&request:User-Name attribute is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
-       chap = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
+       chap = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
        if (!chap) {
-               REDEBUG("You set 'Auth-Type = CHAP' for a request that does not contain a CHAP-Password attribute!");
+               REDEBUG("You set '&control:Auth-Type = CHAP' for a request that "
+                       "does not contain a CHAP-Password attribute!");
                return RLM_MODULE_INVALID;
        }
 
        if (chap->vp_length == 0) {
-               REDEBUG("CHAP-Password is empty");
+               REDEBUG("&request:CHAP-Password is empty");
                return RLM_MODULE_INVALID;
        }
 
        if (chap->vp_length != CHAP_VALUE_LENGTH + 1) {
-               REDEBUG("CHAP-Password has invalid length");
+               REDEBUG("&request:CHAP-Password has invalid length");
                return RLM_MODULE_INVALID;
        }
 
-       /*
-        *      Don't print out the CHAP password here.  It's binary crap.
-        */
-       RDEBUG("Login attempt by \"%s\" with CHAP password",
-               request->username->vp_strvalue);
-
-       if ((passwd_item = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL){
-               if (pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) != NULL){
+       password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+       if (password == NULL) {
+               if (fr_pair_find_by_num(request->config, PW_USER_PASSWORD, 0, TAG_ANY) != NULL){
                        REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                        REDEBUG("!!! Please update your configuration so that the \"known !!!");
                        REDEBUG("!!! good\" cleartext password is in Cleartext-Password,  !!!");
@@ -94,12 +92,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                        REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                }
 
-               REDEBUG("Cleartext password is required for authentication");
-               return RLM_MODULE_INVALID;
+               REDEBUG("&control:Cleartext-Password is required for authentication");
+               return RLM_MODULE_FAIL;
        }
 
-       rad_chap_encode(request->packet, pass_str,
-                       chap->vp_octets[0], passwd_item);
+       rad_chap_encode(request->packet, pass_str, chap->vp_octets[0], password);
 
        if (RDEBUG_ENABLED3) {
                uint8_t const *p;
@@ -107,13 +104,16 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                VALUE_PAIR *vp;
                char buffer[CHAP_VALUE_LENGTH * 2 + 1];
 
-               RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\"", passwd_item->vp_strvalue);
+               RDEBUG3("Comparing with \"known good\" &control:Cleartext-Password value \"%s\"",
+                       password->vp_strvalue);
 
-               vp = pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
                if (vp) {
+                       RDEBUG2("Using challenge from &request:CHAP-Challenge");
                        p = vp->vp_octets;
                        length = vp->vp_length;
                } else {
+                       RDEBUG2("Using challenge from authenticator field");
                        p = request->packet->vector;
                        length = sizeof(request->packet->vector);
                }
@@ -132,14 +132,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                RDEBUG2("Comparing with \"known good\" Cleartext-Password");
        }
 
-       if (rad_digest_cmp(pass_str + 1, chap->vp_octets + 1,
-                          CHAP_VALUE_LENGTH) != 0) {
+       if (rad_digest_cmp(pass_str + 1, chap->vp_octets + 1, CHAP_VALUE_LENGTH) != 0) {
                REDEBUG("Password comparison failed: password is incorrect");
                return RLM_MODULE_REJECT;
        }
 
-       RDEBUG("CHAP user \"%s\" authenticated successfully",
-             request->username->vp_strvalue);
+       RDEBUG("CHAP user \"%s\" authenticated successfully", request->username->vp_strvalue);
 
        return RLM_MODULE_OK;
 }
@@ -155,21 +153,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
  */
 extern module_t rlm_chap;
 module_t rlm_chap = {
-        RLM_MODULE_INIT,
-       "CHAP",
-       0,      /* type */
-        0,
-        NULL,                          /* CONF_PARSER */
-       NULL,                           /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "chap",
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
        },
 };
index 597169c..1361f64 100644 (file)
@@ -4,14 +4,14 @@ rlm_couchbase
 General
 -------
 
-This module supports accounting, authorization, dynamic clients and simultaneous use checking.  Accounting data is written directly to Couchbase as JSON documents and user authorization information is read from JSON documents stored within Couchbase.    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.  To enable simultanous use checking you will need to modify the configuration parameters described below and list the ```couchbase``` module in the ```session`` and ```accounting``` sections of your site configuration.
+This module supports accounting, authorization, dynamic clients and simultaneous use checking.  Accounting data is written directly to Couchbase as JSON documents and user authorization information is read from JSON documents stored within Couchbase.    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.  To enable simultanous use checking you will need to list the ```couchbase``` module in the ```session``` and ```accounting``` sections of your site configuration.
 
 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.
 
 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.
+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 Couchbase cluster.  You have full control over what attributes are recorded and how those attributes are mapped to JSON element names via the module configuration.
 
 This example is from an Aerohive wireless access point.
 
@@ -51,28 +51,28 @@ To generate the 'calledStationSSID' fields you will need to use the ```rewrite_c
 
 ```
 ## simple nt domain regex
-simple_nt_regexp = "^([^\\]*)(\\(.*))$"
+simple_nt_regexp = "^([^\\]*)\\(.*)$"
 
 ## simple nai regex
-simple_nai_regexp = "^([^@]*)(@(.*))$"
+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
-       }
+    if(User-Name && (User-Name =~ /${policy.simple_nt_regexp}/)){
+        update request {
+            &Stripped-User-Domain = "%{1}"
+            &Stripped-User-Name = "%{2}"
+        }
+    }
+    elsif(User-Name && (User-Name =~ /${policy.simple_nai_regexp}/)){
+        update request {
+            &Stripped-User-Name = "%{1}"
+            &Stripped-User-Domain = "%{2}"
+        }
+    }
+    else {
+        noop
+    }
 }
 ```
 
@@ -83,7 +83,9 @@ Authorization
 
 The authorization functionality 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}}}}"```
+```
+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.
 
@@ -139,14 +141,14 @@ function (doc, meta) {
 }
 ```
 
-This is the simplest possible view that would return all documents in the specified bucket having a ```docType``` element withmeta.id ```radclient``` value.  The module only reads the ```id``` element in the returned view thus no additional output is needed and any additional output would be ignored.
+This is the simplest possible view that would return all documents in the specified bucket having a ```docType``` element with ```radclient``` value. The module only reads the ```key``` (first emited field) and ```id``` elements in the returned view thus no additional output is needed and any additional output would be ignored.  The ```key``` emitted here will be used as the client name inside the module.
 
-To have the module load only a subset of the client documents contained within the bucket you could add additional elements to the client documents and then filter based on those elements within your view
+To have the module load only a subset of the client documents contained within the bucket you could add additional elements to the client documents and then filter based on those elements within your view.
 
 Simultaneous Use
 ----------------
 
-The simultaneous use function relies on data stored in the accounting documents.  When a user attempts to authenticate a view request is made to return all accounting type documents for the current user that do not contain a populates ```stopTimestamp``` value.
+The simultaneous use function relies on data stored in the accounting documents.  When a user attempts to authenticate a view request is made to return all accounting type documents for the current user that do not contain a populated ```stopTimestamp``` value.
 
 Example check view:
 
@@ -162,201 +164,26 @@ function (doc, meta) {
 }
 ```
 
-The key (first emitted field) will need to match *EXACTLY* what you set for ```simul_vkey``` in the module configuration described below.  The default xlat value will attempt to return the lower case 'Stripped-User-Name' attribute or 'User-Name' if the stripped version is not available.
+The key (first emitted field) will need to match *EXACTLY* what you set for ```simul_vkey``` in the module configuration.  The default xlat value will attempt to return the lower case 'Stripped-User-Name' attribute or 'User-Name' if the stripped version is not available.
 
-When the total number of keys (sessions) returned is greater than or equal to the ```Simultaneous-Use``` config section value of the current user, the user will be denied access.  When verification is also enabled, each returned key will be fetched and the appropriate information will be passed on to the ```checkrad``` utillity to verify the session status.  If ```checkrad``` determines the session is no longer valid (stale) the session will be updated and closed in Couchbase (if configured) and that session will not be counted against the users login limit.  Further information is available in the configuration file shown below.
+When the total number of keys (sessions) returned is greater than or equal to the ```Simultaneous-Use``` config section value of the current user, the user will be denied access.  When verification is also enabled, each returned key will be fetched and the appropriate information will be passed on to the ```checkrad``` utillity to verify the session status.  If ```checkrad``` determines the session is no longer valid (stale) the session will be updated and closed in Couchbase (if configured) and that session will not be counted against the users login limit.  Further information is available in the module configuration.
 
 To Use
 ------
+Until this module is added to the stable list you will need to explicitly add it to ```src/modules/stable``` before building the server.
+
+```
+echo rlm_couchbase >> src/modules/stable
+```
 
-Pull freeradius-server master and clone this module under src/modules.  Then enable and compile as usual.
-You will also need the following libraries:
+You will also need the following libraries installed where they may be found by the server configuration script.
 
 * [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
--------------
+Once the above steps are complete, simply configure and install as usual.
 
-```
-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'
-               framedProtocol      = 'Framed-Protocol'
-               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'
-       }
+Module Configuration
+--------------------
 
-       # Couchbase document key for user documents (unlang supported)
-       user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"
-
-       # Set to 'yes' to read radius clients from the Couchbase view specified below.
-       # NOTE: Clients will ONLY be read on server startup.
-       #read_clients = no
-
-       #
-       #  Map attribute names to json element names when loading clients.
-       #
-       #  Configuration follows the same rules as the accounting map above.
-       #
-       client {
-               # Couchbase view that should return all available client documents.
-               view = "_design/client/_view/by_id"
-
-               #
-               # Client mappings are in the format:
-               #  <client attribute> = '<element name>'
-               #
-               # Element names should be single quoted.
-               #
-               # The following attributes are required:
-               #  * ipaddr | ipv4addr | ipv6addr - Client IP Address.
-               #  * secret - RADIUS shared secret.
-               #
-               # All attributes usually supported in a client
-               # definition are also supported here.
-               #
-               attribute {
-                       ipaddr                          = 'clientIdentifier'
-                       secret                          = 'clientSecret'
-                       shortname                       = 'clientShortname'
-                       nas_type                        = 'nasType'
-                       virtual_server                  = 'virtualServer'
-                       require_message_authenticator   = 'requireMessageAuthenticator'
-                       limit {
-                               max_connections             = 'maxConnections'
-                               lifetime                    = 'clientLifetime'
-                               idle_timeout                = 'idleTimeout'
-                       }
-               }
-       }
-
-       # Set to 'yes' to enable simultaneous use checking (multiple logins).
-       # NOTE: This will cause the execution of a view request on every check
-       # and may be a performance penalty.
-       #check_simul = no
-
-       # Couchbase view that should return all account documents keyed by username.
-       #simul_view = "_design/acct/_view/by_user"
-
-       # The key to the above view.
-       # NOTE: This will need to match EXACTLY what you emit from your view.
-       #simul_vkey = "%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}"
-
-       # Set to 'yes' to enable verification of the results returned from the above view.
-       # NOTE: This may be an additional performance penalty to the actual check and
-       # should be avoided unless absolutely neccessary.
-       #verify_simul = no
-
-       # Remove stale session if checkrad does not see a double login.
-       # NOTE: This will only be executed if both check_simul and verify_simul
-       # are set to 'yes' above.
-       #delete_stale_sessions = yes
-
-       #
-       #  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.
-       }
-}
-```
+Please see [/raddb/mods-available/couchbase](/raddb/mods-available/couchbase) for all available configuration options.
index 6d5e1d1..490c9ec 100644 (file)
@@ -1,8 +1,14 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
+/* json.h is at json-c/json.h relative to include dir */
+#undef HAVE_JSONMC_JSON_H
+
 /* Define to 1 if you have the `json_c_version' function. */
 #undef HAVE_JSON_C_VERSION
 
+/* json.h is at json/json.h relative to include dir */
+#undef HAVE_JSON_JSON_H
+
 /* Define to 1 if you have the `json_object_get_string_len' function. */
 #undef HAVE_JSON_OBJECT_GET_STRING_LEN
 
index d31392d..e567fb1 100755 (executable)
 
 
        have_json="yes"
-
-       smart_prefix="json-c json"
        smart_try_dir="$jsonc_include_dir"
 
 
 
-ac_safe=`echo "json.h" | sed 'y%./+-%__pm%'`
+ac_safe=`echo "json/json.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir="/usr/local/include /opt/include"
+
+_smart_try_dir=
+_smart_include_dir=
+
+for _prefix in $smart_prefix ""; do
+  for _dir in $smart_try_dir; do
+    _smart_try_dir="${_smart_try_dir} ${_dir}/${_prefix}"
+  done
+
+  for _dir in $smart_include_dir; do
+    _smart_include_dir="${_smart_include_dir} ${_dir}/${_prefix}"
+  done
+done
+
+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
+  for _prefix in $smart_prefix; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/json/json.h" >&5
+$as_echo_n "checking for ${_prefix}/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="-isystem ${_prefix}/"
+                    { $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
+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
+
+  for prefix in $smart_prefix; do
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file="${_prefix}/${1}"
+
+  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\""
+
+  done
+
+
+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; 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
+
+smart_prefix=
+
+       if test "x$ac_cv_header_json_json_h" != "xyes"; then
+
+
+ac_safe=`echo "json-c/json.h" | sed 'y%./+-%__pm%'`
 old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir="/usr/local/include /opt/include"
@@ -2917,13 +3142,13 @@ done
 
 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.h in $try" >&5
-$as_echo_n "checking for json.h in $try... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json-c/json.h in $try" >&5
+$as_echo_n "checking for json-c/json.h in $try... " >&6; }
     CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <json.h>
+                   #include <json-c/json.h>
 int
 main ()
 {
@@ -2953,13 +3178,13 @@ fi
 
 if test "x$smart_include" = "x"; then
   for _prefix in $smart_prefix; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/json.h" >&5
-$as_echo_n "checking for ${_prefix}/json.h... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/json-c/json.h" >&5
+$as_echo_n "checking for ${_prefix}/json-c/json.h... " >&6; }
 
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <json.h>
+                   #include <json-c/json.h>
 int
 main ()
 {
@@ -2987,13 +3212,13 @@ 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 json.h" >&5
-$as_echo_n "checking for json.h... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json-c/json.h" >&5
+$as_echo_n "checking for json-c/json.h... " >&6; }
 
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <json.h>
+                   #include <json-c/json.h>
 int
 main ()
 {
@@ -3054,7 +3279,7 @@ eval "_smart_include_dir=\"\$_smart_include_dir $DIRS\""
 
 if test "x$LOCATE" != "x"; then
         DIRS=
-  file=json.h
+  file=json-c/json.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
                                         base=`echo $x | sed "s%/${file}%%"`
@@ -3079,13 +3304,13 @@ eval "_smart_include_dir=\"\$_smart_include_dir $DIRS\""
 
 
   for try in $_smart_include_dir; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json.h in $try" >&5
-$as_echo_n "checking for json.h in $try... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json-c/json.h in $try" >&5
+$as_echo_n "checking for json-c/json.h in $try... " >&6; }
     CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <json.h>
+                   #include <json-c/json.h>
 int
 main ()
 {
@@ -3121,10 +3346,20 @@ fi
 
 smart_prefix=
 
-       if test "x$ac_cv_header_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
+               if test "x$ac_cv_header_jsonmc_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;}
+                       fail="$fail json.h"
+               else
+
+$as_echo "#define HAVE_JSONMC_JSON_H 1" >>confdefs.h
+
+               fi
+       else
+
+$as_echo "#define HAVE_JSON_JSON_H 1" >>confdefs.h
+
        fi
 
 
index c126e6e..a43ee2f 100644 (file)
@@ -70,13 +70,19 @@ if test x$with_[]modname != xno; then
        dnl ############################################################
 
        have_json="yes"
-
-       smart_prefix="json-c json"
        smart_try_dir="$jsonc_include_dir"
-       FR_SMART_CHECK_INCLUDE([json.h])
-       if test "x$ac_cv_header_json_h" != "xyes"; then
-               have_json="no"
-               AC_MSG_WARN([json-c headers not found. Use --with-jsonc-include-dir=<path>.])
+       FR_SMART_CHECK_INCLUDE([json/json.h])
+       if test "x$ac_cv_header_json_json_h" != "xyes"; then
+               FR_SMART_CHECK_INCLUDE([json-c/json.h])
+               if test "x$ac_cv_header_jsonmc_json_h" != "xyes"; then
+                       have_json="no"
+                       AC_MSG_WARN([json-c headers not found. Use --with-jsonc-include-dir=<path>.])
+                       fail="$fail json.h"
+               else
+                       AC_DEFINE([HAVE_JSONMC_JSON_H],[1],[json.h is at json-c/json.h relative to include dir])
+               fi
+       else
+               AC_DEFINE([HAVE_JSON_JSON_H],[1],[json.h is at json/json.h relative to include dir])
        fi
 
        dnl ############################################################
index 42942b5..a278dca 100644 (file)
  * @copyright 2013-2014 The FreeRADIUS Server Project.
  */
 
-RCSID("$Id$");
+RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 
 #include <libcouchbase/couchbase.h>
-#include <json.h>
 
 #include "couchbase.h"
 #include "jsonc_missing.h"
@@ -261,7 +260,7 @@ lcb_error_t couchbase_server_stats(lcb_t instance, const void *cookie)
 
        /* get statistics */
        if ((error = lcb_server_stats(instance, cookie, 1, commands)) == LCB_SUCCESS) {
-               /* enter event look on sucess */
+               /* enter event look on success */
                lcb_wait(instance);
        }
 
index f1c0109..1e1ded4 100644 (file)
 #ifndef _couchbase_h_
 #define _couchbase_h_
 
-RCSIDH(couchbase_h, "$Id$");
+RCSIDH(couchbase_h, "$Id$")
 
 #include <libcouchbase/couchbase.h>
-#include <json.h>
+
+#include "jsonc_missing.h"
 
 /** Information relating to the parsing of Couchbase document payloads
  *
index 3eaf8e2..dc3e7f6 100644 (file)
@@ -24,7 +24,7 @@
  * @copyright 2013-2014 The FreeRADIUS Server Project.
  */
 
-RCSID("$Id$");
+RCSID("$Id$")
 
 #include <string.h>
 
@@ -38,9 +38,9 @@ RCSID("$Id$");
 
 #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)
+       if ((obj == NULL) || (json_object_get_type(obj) != json_type_string))
                return 0;
-       return (int)strlen(json_object_to_json_string(obj));
+       return (int)strlen(json_object_get_string(obj));
 }
 #endif
 
index 288e5eb..5df68a8 100644 (file)
 #ifndef _jsonc_missing_h_
 #define _jsonc_missing_h_
 
-RCSIDH(jsonc_missing_h, "$Id$");
-
-#include <json.h>
+RCSIDH(jsonc_missing_h, "$Id$")
 
 #include "config.h"
 
+#if defined(HAVE_JSONMC_JSON_H)
+#  include <json-c/json.h>
+#elif defined(HAVE_JSON_JSON_H)
+#  include <json/json.h>
+#endif
+
 #ifndef HAVE_JSON_C_VERSION
 const char *json_c_version(void);
 #endif
@@ -65,7 +69,8 @@ enum json_tokener_error json_tokener_get_error(json_tokener *tok);
 /* 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; \
+       char *key = NULL; \
+       struct json_object *val = NULL; \
        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; \
@@ -73,7 +78,9 @@ enum json_tokener_error json_tokener_get_error(json_tokener *tok);
                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; \
+       char *key = NULL; \
+       struct json_object *val = NULL; \
+       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, \
index 942419f..c5b721d 100644 (file)
  * @copyright 2013-2014 The FreeRADIUS Server Project.
  */
 
-RCSID("$Id$");
+RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 
 #include <libcouchbase/couchbase.h>
-#include <json.h>
 
 #include "mod.h"
 #include "couchbase.h"
@@ -69,7 +68,7 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
        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 */
+       lcb_error_t cb_error;                   /* couchbase error status */
 
        /* create instance */
        cb_error = couchbase_init_connection(&cb_inst, inst->server, inst->bucket, inst->password);
@@ -103,34 +102,7 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
        return chandle;
 }
 
-/** Check the health of a connection handle
- *
- * Attempt to determing the state of the Couchbase connection by requesting
- * a cluster statistics report.  Mark the connection as failed if the request
- * returns anything other than success.
- *
- * @param  instance The module instance (currently unused).
- * @param  handle   The connection handle.
- * @return          Returns 0 on success (alive) and -1 on error (unavailable).
- */
-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 stats */
-       if ((cb_error = couchbase_server_stats(cb_inst, NULL)) != LCB_SUCCESS) {
-               /* log error */
-               ERROR("rlm_couchbase: failed to get couchbase server stats: %s (0x%x)",
-                     lcb_strerror(NULL, cb_error), cb_error);
-               /* error out */
-               return -1;
-       }
-       return 0;
-}
-
-/** Build a JSON object map from the configuration "map" section
+/** Build a JSON object map from the configuration "update" section
  *
  * Parse the "map" section from the module configuration file and store this
  * as a JSON object (key/value list) in the module instance.  This map will be
@@ -148,12 +120,18 @@ int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance)
        CONF_PAIR *cp;                      /* conig pair */
        const char *attribute, *element;    /* attribute and element names */
 
-       /* find map section */
-       cs = cf_section_sub_find(conf, "map");
+       /* find update section */
+       cs = cf_section_sub_find(conf, "update");
+
+       /* backwards compatibility */
+       if (!cs) {
+               cs = cf_section_sub_find(conf, "map");
+               WARN("rlm_couchbase: found deprecated 'map' section - please change to 'update'");
+       }
 
        /* check section */
        if (!cs) {
-               ERROR("rlm_couchbase: failed to find 'map' section in config");
+               ERROR("rlm_couchbase: failed to find 'update' section in config");
                /* fail */
                return -1;
        }
@@ -165,7 +143,7 @@ int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance)
        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");
+                       ERROR("rlm_couchbase: failed to parse invalid item in 'update' section");
                        /* free map */
                        if (inst->map) {
                                json_object_put(inst->map);
@@ -177,21 +155,21 @@ int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance)
                /* get value pair from item */
                cp = cf_item_to_pair(ci);
 
-               /* get pair name (element name) */
-               element = cf_pair_attr(cp);
+               /* get pair name (attribute name) */
+               attribute = cf_pair_attr(cp);
 
-               /* get pair value (attribute name) */
-               attribute = cf_pair_value(cp);
+               /* get pair value (element name) */
+               element = cf_pair_value(cp);
 
                /* add pair name and value */
                json_object_object_add(inst->map, attribute, json_object_new_string(element));
 
                /* debugging */
-               DEBUG3("rlm_couchbase: added attribute '%s' to element '%s' map to object", attribute, element);
+               DEBUG3("rlm_couchbase: added attribute '%s' to element '%s' mapping", attribute, element);
        }
 
        /* debugging */
-       DEBUG3("rlm_couchbase: built attribute to element map %s", json_object_to_json_string(inst->map));
+       DEBUG3("rlm_couchbase: built attribute to element mapping %s", json_object_to_json_string(inst->map));
 
        /* return */
        return 0;
@@ -266,19 +244,19 @@ 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 *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 */
+       TALLOC_CTX *ctx;                    /* talloc context for fr_pair_make */
+       VALUE_PAIR *vp, **ptr;              /* value pair and value pair pointer for fr_pair_make */
 
-       /* assign ctx and vps for pairmake based on section */
+       /* assign ctx and vps for fr_pair_make based on section */
        if (strcmp(section, "config") == 0) {
                ctx = request;
-               ptr = &(request->config_items);
+               ptr = &(request->config);
        } 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");
+               RERROR("invalid section passed for fr_pair_make");
                /* return */
                return NULL;
        }
@@ -286,7 +264,7 @@ void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQ
        /* 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)) {
+               if ((jobj == NULL) || !json_object_is_type(jobj, json_type_object)) {
                        /* log error */
                        RERROR("invalid json type for '%s' section - sections must be json objects", section);
                        /* reuturn */
@@ -295,7 +273,7 @@ void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQ
                /* 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)) {
+                       if ((jobj == NULL) || !json_object_is_type(json_vp, json_type_object)) {
                                /* log error */
                                RERROR("invalid json type for '%s' attribute - attributes must be json objects",
                                       attribute);
@@ -308,6 +286,8 @@ void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQ
                        /* create pair from json object */
                        if (json_object_object_get_ex(json_vp, "value", &jval) &&
                                json_object_object_get_ex(json_vp, "op", &jop)) {
+                               /* check for null before getting type */
+                               if (jval == NULL) return NULL;
                                /* make correct pairs based on json object type */
                                switch (json_object_get_type(jval)) {
                                case json_type_double:
@@ -316,7 +296,7 @@ void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQ
                                        /* debugging */
                                        RDEBUG("adding '%s' attribute to '%s' section", attribute, section);
                                        /* add pair */
-                                       vp = pairmake(ctx, ptr, attribute, json_object_get_string(jval),
+                                       vp = fr_pair_make(ctx, ptr, attribute, json_object_get_string(jval),
                                                fr_str2int(fr_tokens, json_object_get_string(jop), 0));
                                        /* check pair */
                                        if (!vp) {
@@ -398,7 +378,7 @@ json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp)
                        /* return as 64 bit int - JSON spec does not support unsigned ints */
                        return json_object_new_int(i);
 #endif
-               break;
+
                case PW_TYPE_SIGNED:
 #ifdef HAVE_JSON_OBJECT_NEW_INT64
                        /* debug */
@@ -410,7 +390,7 @@ json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp)
                        /* 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 */
@@ -420,8 +400,9 @@ json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp)
 #else
                        /* warning */
                        RWARN("skipping 64 bit integer attribute '%s' - please upgrade json-c to 0.10+", vp->da->name);
+                       break;
 #endif
-               break;
+
                default:
                        /* silence warnings - do nothing */
                break;
@@ -478,7 +459,7 @@ int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
        }
 
        /* get current event timestamp */
-       if ((vp = pairfind(vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
                /* get seconds value from attribute */
                ts = vp->vp_date;
        } else {
@@ -492,7 +473,7 @@ int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
        memset(value, 0, sizeof(value));
 
        /* get elapsed session time */
-       if ((vp = pairfind(vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY)) != NULL) {
                /* calculate diff */
                ts = (ts - vp->vp_integer);
                /* calculate start time */
@@ -515,74 +496,27 @@ int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps)
        return 0;
 }
 
-/** Iterate over all client attribute pairs and create client pair data using JSON element names
- *
- * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS as well to support nested
- * configurations sections.
+/** Handle client value processing for client_map_section()
  *
- * @param client The new client config section using the mapped names.
- * @param map    The client attribute section from the module configuration.
- * @param json   JSON object representation of a client document fetched from Couchbase.
- * @param docid  Document id.
- * @return       Returns 0 on success, -1 on error.
+ * @param  out  Character output
+ * @param  cp   Configuration pair
+ * @param  data The client data
+ * @return      Returns 0 on success, -1 on error.
  */
-int mod_client_map_section(CONF_SECTION *client, CONF_SECTION const *map,
-                           json_object *json, char const *docid)
+static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
 {
-       CONF_ITEM const *ci;
-
-       for (ci = cf_item_find_next(map, NULL); ci != NULL; ci = cf_item_find_next(map, ci)) {
-               CONF_PAIR const *cp;
-               char const *attribute;
-               char const *element;
-               json_object *jval;
-
-               /*
-                * Recursively process map subsection
-                */
-               if (cf_item_is_section(ci)) {
-                       CONF_SECTION *cs, *cc;    /* local scoped for new section */
-
-                       cs = cf_item_to_section(ci);
-                       cc = cf_section_alloc(client, cf_section_name1(cs), cf_section_name2(cs));
-                       if (!cc) return -1;
-
-                       cf_section_add(client, cc);
-
-                       if (mod_client_map_section(cc, cs, json, docid) != 0) {
-                               return -1;
-                       }
-                       /* continue on to the next item */
-                       continue;
-               }
+       json_object *jval;
 
-               /* create pair from item and get attribute name and value */
-               cp = cf_item_to_pair(ci);
-               attribute = cf_pair_attr(cp);
-               element = cf_pair_value(cp);
-
-               /* attempt to find element in json object */
-               if (!json_object_object_get_ex(json, element, &jval)) {
-                       /* skip this item */
-                       continue;
-               }
+       if (!json_object_object_get_ex((json_object *)data, cf_pair_value(cp), &jval)) {
+               *out = NULL;
+               return 0;
+       }
 
-               /* allocate config pair */
-               cp = cf_pair_alloc(client, attribute, json_object_get_string(jval),
-                                  T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
+       if (!jval) return -1;
 
-               /* check pair */
-               if (!cp) {
-                       ERROR("rlm_couchbase: failed allocating config pair '%s' = '%s'", attribute,
-                             json_object_get_string(jval));
-                       return -1;
-               }
+       *out = talloc_strdup(NULL, json_object_get_string(jval));
+       if (!*out) return -1;
 
-               /* add pair to section */
-               cf_item_add(client, cf_pair_to_item(cp));
-       }
-
-       /* return success */
        return 0;
 }
 
@@ -595,21 +529,22 @@ int mod_client_map_section(CONF_SECTION *client, CONF_SECTION const *map,
  * run once at sever startup this should not be a concern.
  *
  * @param  inst The module instance.
- * @param  cs   The client attribute configuration section.
+ * @param  tmpl Default values for new clients.
+ * @param  map  The client attribute configuration section.
  * @return      Returns 0 on success, -1 on error.
  */
-int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *cs)
+int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *tmpl, CONF_SECTION *map)
 {
        rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */
-       char vpath[256], docid[MAX_KEY_SIZE];  /* view path and document id */
-       char error[512];                       /* view error return */
-       int idx = 0;                           /* row array index counter */
-       int retval = 0;                        /* return value */
-       lcb_error_t cb_error = LCB_SUCCESS;    /* couchbase error holder */
-       json_object *json, *jval;              /* json object holders */
-       json_object *jrows = NULL;             /* json object to hold view rows */
-       CONF_SECTION *client;                  /* freeradius config section */
-       RADCLIENT *c;                          /* freeradius client */
+       char vpath[256], vid[MAX_KEY_SIZE], vkey[MAX_KEY_SIZE];  /* view path and fields */
+       char error[512];                                         /* view error return */
+       int idx = 0;                                             /* row array index counter */
+       int retval = 0;                                          /* return value */
+       lcb_error_t cb_error = LCB_SUCCESS;                      /* couchbase error holder */
+       json_object *json, *jval;                                /* json object holders */
+       json_object *jrows = NULL;                               /* json object to hold view rows */
+       CONF_SECTION *client;                                    /* freeradius config section */
+       RADCLIENT *c;                                            /* freeradius client */
 
        /* get handle */
        handle = fr_connection_get(inst->pool);
@@ -684,7 +619,7 @@ int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *cs)
        DEBUG3("rlm_couchbase: jrows == %s", json_object_to_json_string(jrows));
 
        /* check for valid row value */
-       if (!json_object_is_type(jrows, json_type_array) || json_object_array_length(jrows) < 1) {
+       if ((jrows == NULL) || !json_object_is_type(jrows, json_type_array) || json_object_array_length(jrows) < 1) {
                /* log error */
                ERROR("rlm_couchbase: no valid rows returned from view: %s", vpath);
                /* set return */
@@ -698,26 +633,38 @@ int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *cs)
                /* fetch current index */
                json = json_object_array_get_idx(jrows, idx);
 
-               /* get document id */
+               /* get view id */
                if (json_object_object_get_ex(json, "id", &jval)) {
-                       /* clear docid */
-                       memset(docid, 0, sizeof(docid));
+                       /* clear view id */
+                       memset(vid, 0, sizeof(vid));
                        /* copy and check length */
-                       if (strlcpy(docid, json_object_get_string(jval), sizeof(docid)) >= sizeof(docid)) {
-                               ERROR("rlm_couchbase: document id from row longer than MAX_KEY_SIZE (%d)",
+                       if (strlcpy(vid, json_object_get_string(jval), sizeof(vid)) >= sizeof(vid)) {
+                               ERROR("rlm_couchbase: id from row longer than MAX_KEY_SIZE (%d)",
                                      MAX_KEY_SIZE);
                                continue;
                        }
+               } else {
+                       WARN("rlm_couchbase: failed to fetch id from row - skipping");
+                       continue;
                }
 
-               /* check for valid doc id */
-               if (docid[0] == 0) {
-                       WARN("rlm_couchbase: failed to fetch document id from row - skipping");
+               /* get view key */
+               if (json_object_object_get_ex(json, "key", &jval)) {
+                       /* clear view key */
+                       memset(vkey, 0, sizeof(vkey));
+                       /* copy and check length */
+                       if (strlcpy(vkey, json_object_get_string(jval), sizeof(vkey)) >= sizeof(vkey)) {
+                               ERROR("rlm_couchbase: key from row longer than MAX_KEY_SIZE (%d)",
+                                     MAX_KEY_SIZE);
+                               continue;
+                       }
+               } else {
+                       WARN("rlm_couchbase: failed to fetch key from row - skipping");
                        continue;
                }
 
                /* fetch document */
-               cb_error = couchbase_get_key(cb_inst, cookie, docid);
+               cb_error = couchbase_get_key(cb_inst, cookie, vid);
 
                /* check error and object */
                if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
@@ -733,9 +680,10 @@ int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *cs)
                DEBUG3("rlm_couchbase: cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
 
                /* allocate conf section */
-               client = cf_section_alloc(NULL, "client", docid);
+               client = tmpl ? cf_section_dup(NULL, tmpl, "client", vkey, true) :
+                               cf_section_alloc(NULL, "client", vkey);
 
-               if (mod_client_map_section(client, cs, cookie->jobj, docid) != 0) {
+               if (client_map_section(client, map, _get_client_value, cookie->jobj) < 0) {
                        /* free config setion */
                        talloc_free(client);
                        /* set return */
@@ -765,7 +713,7 @@ int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *cs)
 
                /* attempt to add client */
                if (!client_add(NULL, c)) {
-                       ERROR("rlm_couchbase: failed to add client from %s, possible duplicate?", docid);
+                       ERROR("rlm_couchbase: failed to add client '%s' from '%s', possible duplicate?", vkey, vid);
                        /* free client */
                        client_free(c);
                        /* set return */
index 750bedb..5f40db9 100644 (file)
 #ifndef _mod_h_
 #define _mod_h_
 
-RCSIDH(mod_h, "$Id$");
+RCSIDH(mod_h, "$Id$")
 
 #include <freeradius-devel/radiusd.h>
 
 #include <libcouchbase/couchbase.h>
-#include <json.h>
 
 #include "jsonc_missing.h"
 
@@ -85,8 +84,6 @@ typedef struct rlm_couchbase_handle_t {
 /* define functions */
 void *mod_conn_create(TALLOC_CTX *ctx, void *instance);
 
-int mod_conn_alive(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);
@@ -99,6 +96,6 @@ int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps);
 
 int mod_client_map_section(CONF_SECTION *client, CONF_SECTION const *map, json_object *json, char const *docid);
 
-int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *cs);
+int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *tmpl, CONF_SECTION *map);
 
 #endif /* _mod_h_ */
index f7afb4c..a73244c 100644 (file)
@@ -24,7 +24,7 @@
  * @copyright 2013-2014 The FreeRADIUS Server Project.
  */
 
-RCSID("$Id$");
+RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/libradius.h>
@@ -32,7 +32,6 @@ RCSID("$Id$");
 #include <freeradius-devel/rad_assert.h>
 
 #include <libcouchbase/couchbase.h>
-#include <json.h>
 
 #include "mod.h"
 #include "couchbase.h"
@@ -43,7 +42,7 @@ RCSID("$Id$");
  */
 static const CONF_PARSER client_config[] = {
        { "view", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_couchbase_t, client_view), "_design/client/_view/by_name" },
-       {NULL, -1, 0, NULL, NULL}     /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 /**
@@ -67,7 +66,7 @@ static const CONF_PARSER module_config[] = {
        { "simul_vkey", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_couchbase_t, simul_vkey), "%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}" },
        { "verify_simul", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_couchbase_t, verify_simul), NULL }, /* NULL defaults to "no" */
 #endif
-       {NULL, -1, 0, NULL, NULL}     /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 /** Initialize the rlm_couchbase module
@@ -127,7 +126,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 
        /* initiate connection pool */
-       inst->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, mod_conn_alive, NULL);
+       inst->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, NULL);
 
        /* check connection pool */
        if (!inst->pool) {
@@ -138,7 +137,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        /* load clients if requested */
        if (inst->read_clients) {
-               CONF_SECTION *cs; /* conf section */
+               CONF_SECTION *cs, *map, *tmpl; /* conf section */
 
                /* attempt to find client section */
                cs = cf_section_sub_find(conf, "client");
@@ -148,20 +147,21 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                        return -1;
                }
 
-
                /* attempt to find attribute subsection */
-               cs = cf_section_sub_find(cs, "attribute");
-               if (!cs) {
+               map = cf_section_sub_find(cs, "attribute");
+               if (!map) {
                        ERROR("rlm_couchbase: failed to find attribute subsection while loading clients");
                        /* fail */
                        return -1;
                }
 
+               tmpl = cf_section_sub_find(cs, "template");
+
                /* debugging */
                DEBUG("rlm_couchbase: preparing to load client documents");
 
                /* attempt to load clients */
-               if (mod_load_client_documents(inst, cs) != 0) {
+               if (mod_load_client_documents(inst, tmpl, map) != 0) {
                        /* fail */
                        return -1;
                }
@@ -223,7 +223,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                /* set return */
                rcode = RLM_MODULE_FAIL;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* debugging */
@@ -235,7 +235,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        /* inject reply value pairs defined in this json oblect */
        mod_json_object_to_value_pairs(cookie->jobj, "reply", request);
 
-       free_and_return:
+       finish:
 
        /* free json object */
        if (cookie->jobj) {
@@ -283,7 +283,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        rad_assert(request->packet != NULL);
 
        /* sanity check */
-       if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
                /* log debug */
                RDEBUG("could not find status type in packet");
                /* return */
@@ -320,7 +320,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                /* set return */
                rcode = RLM_MODULE_NOOP;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* attempt to fetch document */
@@ -360,7 +360,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        switch (status) {
        case PW_STATUS_START:
                /* add start time */
-               if ((vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_num(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));
@@ -369,7 +369,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 
        case PW_STATUS_STOP:
                /* add stop time */
-               if ((vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_num(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));
@@ -387,7 +387,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                /* don't doing anything */
                rcode = RLM_MODULE_NOOP;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* loop through pairs and add to json document */
@@ -408,7 +408,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                /* set return */
                rcode = RLM_MODULE_FAIL;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* debugging */
@@ -422,8 +422,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                RERROR("failed to store document (%s): %s (0x%x)", dockey, lcb_strerror(NULL, cb_error), cb_error);
        }
 
-       free_and_return:
-
+finish:
        /* free and reset json object */
        if (cookie->jobj) {
                json_object_put(cookie->jobj);
@@ -523,7 +522,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                /* set return */
                rcode = RLM_MODULE_FAIL;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* debugging */
@@ -545,7 +544,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                /* set return */
                rcode = RLM_MODULE_FAIL;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* check for document id in return */
@@ -555,7 +554,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                /* set return */
                rcode = RLM_MODULE_FAIL;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* get and hold rows */
@@ -574,7 +573,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                /* set return */
                rcode = RLM_MODULE_FAIL;
                /* return */
-               goto free_and_return;
+               goto finish;
        }
 
        /* debugging */
@@ -589,7 +588,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
        /* check count */
        if (request->simul_count < request->simul_max) {
                rcode = RLM_MODULE_OK;
-               goto free_and_return;
+               goto finish;
        }
 
        /*
@@ -598,7 +597,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
         */
        if (inst->verify_simul != true) {
                rcode = RLM_MODULE_OK;
-               goto free_and_return;
+               goto finish;
        }
 
        /* debugging */
@@ -608,24 +607,25 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
        request->simul_count = 0;
 
        /* get client ip address for MPP detection below */
-       if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
                client_ip_addr = vp->vp_ipaddr;
        }
 
        /* get calling station id for MPP detection below */
-       if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
                client_cs_id = vp->vp_strvalue;
        }
 
        /* loop across all row elements */
        for (idx = 0; idx < json_object_array_length(jrows); idx++) {
+               /* clear docid */
+               memset(docid, 0, sizeof(docid));
+
                /* fetch current index */
                json = json_object_array_get_idx(jrows, idx);
 
                /* get document id */
                if (json_object_object_get_ex(json, "id", &jval)) {
-                       /* clear docid */
-                       memset(docid, 0, sizeof(docid));
                        /* copy and check length */
                        if (strlcpy(docid, json_object_get_string(jval), sizeof(docid)) >= sizeof(docid)) {
                                RERROR("document id from row longer than MAX_KEY_SIZE (%d)", MAX_KEY_SIZE);
@@ -649,7 +649,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                        /* set return */
                        rcode = RLM_MODULE_FAIL;
                        /* return */
-                       goto free_and_return;
+                       goto finish;
                }
 
                /* debugging */
@@ -661,14 +661,14 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                        if (!json_object_object_get_ex(cookie->jobj, element, &jval)){
                                RDEBUG("cannot zap stale entry without username");
                                rcode = RLM_MODULE_FAIL;
-                               goto free_and_return;
+                               goto finish;
                        }
                        /* copy json string value to user_name */
                        user_name = talloc_typed_strdup(request, json_object_get_string(jval));
                } else {
                        RDEBUG("failed to find map entry for User-Name attribute");
                        rcode = RLM_MODULE_FAIL;
-                       goto free_and_return;
+                       goto finish;
                }
 
                /* get element name for Acct-Session-Id attribute */
@@ -677,14 +677,14 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                        if (!json_object_object_get_ex(cookie->jobj, element, &jval)){
                                RDEBUG("cannot zap stale entry without session id");
                                rcode = RLM_MODULE_FAIL;
-                               goto free_and_return;
+                               goto finish;
                        }
                        /* copy json string value to session_id */
                        session_id = talloc_typed_strdup(request, json_object_get_string(jval));
                } else {
                        RDEBUG("failed to find map entry for Acct-Session-Id attribute");
                        rcode = RLM_MODULE_FAIL;
-                       goto free_and_return;
+                       goto finish;
                }
 
                /* get element name for NAS-IP-Address attribute */
@@ -780,26 +780,17 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                        /* check failed - return error */
                        REDEBUG("failed to check the terminal server for user '%s'", user_name);
                        rcode = RLM_MODULE_FAIL;
-                       goto free_and_return;
+                       goto finish;
                }
 
                /* free and reset document user name talloc */
-               if (user_name) {
-                       talloc_free(user_name);
-                       user_name = NULL;
-               }
+               if (user_name) TALLOC_FREE(user_name);
 
                /* free and reset document calling station id talloc */
-               if (cs_id) {
-                       talloc_free(cs_id);
-                       cs_id = NULL;
-               }
+               if (cs_id) TALLOC_FREE(cs_id);
 
                /* free and reset document session id talloc */
-               if (session_id) {
-                       talloc_free(session_id);
-                       session_id = NULL;
-               }
+               if (session_id) TALLOC_FREE(session_id);
 
                /* free and reset json object before fetching next row */
                if (cookie->jobj) {
@@ -809,30 +800,16 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
        }
 
        /* debugging */
-       RDEBUG("retained %d open sessions for %s after verification",
+       RDEBUG("Retained %d open sessions for %s after verification",
               request->simul_count, request->username->vp_strvalue);
 
-       free_and_return:
-
-       /* free document user name talloc */
-       if (user_name) {
-               talloc_free(user_name);
-       }
-
-       /* free document calling station id talloc */
-       if (cs_id) {
-               talloc_free(cs_id);
-       }
-
-       /* free document session id talloc */
-       if (session_id) {
-               talloc_free(session_id);
-       }
+finish:
+       if (user_name) talloc_free(user_name);
+       if (cs_id) talloc_free(cs_id);
+       if (session_id) talloc_free(session_id);
 
        /* free rows */
-       if (jrows) {
-               json_object_put(jrows);
-       }
+       if (jrows) json_object_put(jrows);
 
        /* free and reset json object */
        if (cookie->jobj) {
@@ -840,10 +817,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
                cookie->jobj = NULL;
        }
 
-       /* release handle */
-       if (handle) {
-               fr_connection_release(inst->pool, handle);
-       }
+       if (handle) fr_connection_release(inst->pool, handle);
 
        /*
         * The Auth module apparently looks at request->simul_count,
@@ -863,19 +837,11 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
  */
 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);
-       }
+       rlm_couchbase_t *inst = instance;
 
-       /* destroy connection pool */
-       if (inst->pool) {
-               fr_connection_pool_delete(inst->pool);
-       }
+       if (inst->map) json_object_put(inst->map);
+       if (inst->pool) fr_connection_pool_free(inst->pool);
 
-       /* return okay */
        return 0;
 }
 
@@ -884,29 +850,20 @@ static int mod_detach(void *instance)
  */
 extern module_t rlm_couchbase;
 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 */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "couchbase",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_couchbase_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
 #ifdef WITH_ACCOUNTING
-               mod_accounting,         /* accounting */
-#else
-               NULL,
+               [MOD_ACCOUNTING]        = mod_accounting,
 #endif
 #ifdef WITH_SESSION_MGMT
-               mod_checksimul,         /* checksimul */
-#else
-               NULL,
+               [MOD_SESSION]           = mod_checksimul
 #endif
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
        },
 };
index 1beef29..8de31a1 100755 (executable)
@@ -43,7 +43,7 @@ EOF
 #
 #  Print out only one user,
 #
-#  Or specifiy printing in hours, minutes, or seconds (default)
+#  Or specify printing in hours, minutes, or seconds (default)
 #
 GetOptions ('user=s'  => \$user,
            'match=s' => \$match,
index eff813b..7f19824 100644 (file)
@@ -135,8 +135,7 @@ static const CONF_PARSER module_config[] = {
 
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -161,7 +160,7 @@ static int counter_cmp(void *instance, UNUSED REQUEST *req, VALUE_PAIR *request,
        /*
         *      Find the key attribute.
         */
-       key_vp = pair_find_by_da(request, inst->key_attr, TAG_ANY);
+       key_vp = fr_pair_find_by_da(request, inst->key_attr, TAG_ANY);
        if (!key_vp) {
                return RLM_MODULE_NOOP;
        }
@@ -324,6 +323,58 @@ static int find_next_reset(rlm_counter_t *inst, time_t timeval)
 }
 
 
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
+{
+       rlm_counter_t *inst = instance;
+       ATTR_FLAGS flags;
+       DICT_ATTR const *da;
+
+       memset(&flags, 0, sizeof(flags));
+       flags.compare = 1;      /* ugly hack */
+       da = dict_attrbyname(inst->counter_name);
+       if (da && (da->type != PW_TYPE_INTEGER)) {
+               cf_log_err_cs(conf, "Counter attribute %s MUST be integer", inst->counter_name);
+               return -1;
+       }
+
+       if (!da && (dict_addattr(inst->counter_name, -1, 0, PW_TYPE_INTEGER, flags) < 0)) {
+               cf_log_err_cs(conf, "Failed to create counter attribute %s: %s", inst->counter_name, fr_strerror());
+               return -1;
+       }
+
+       if (paircompare_register_byname(inst->counter_name, NULL, true, counter_cmp, inst) < 0) {
+               cf_log_err_cs(conf, "Failed to create counter attribute %s: %s", inst->counter_name, fr_strerror());
+               return -1;
+       }
+
+
+       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 = da;
+
+       /*
+        *      Create a new attribute for the check item.
+        */
+       flags.compare = 0;
+       if (dict_addattr(inst->check_name, -1, 0, PW_TYPE_INTEGER, flags) < 0) {
+               cf_log_err_cs(conf, "Failed to create check attribute %s: %s", inst->counter_name, fr_strerror());
+               return -1;
+
+       }
+
+       da = dict_attrbyname(inst->check_name);
+       if (!da) {
+               cf_log_err_cs(conf, "Failed to find check attribute %s", inst->counter_name);
+               return -1;
+       }
+       inst->check_attr = da;
+
+       return 0;
+}
+
 /*
  *     Do any per-module initialization that is separate to each
  *     configured instance of the module.  e.g. set up connections
@@ -339,7 +390,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        rlm_counter_t *inst = instance;
        DICT_ATTR const *da;
        DICT_VALUE *dval;
-       ATTR_FLAGS flags;
        time_t now;
        int cache_size;
        int ret;
@@ -380,40 +430,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 
        /*
-        *  Create a new attribute for the counter.
-        */
-       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());
-               return -1;
-       }
-
-       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 = 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());
-               return -1;
-
-       }
-       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 = da;
-
-       /*
         * Find the attribute for the allowed protocol
         */
        if (inst->service_type != NULL) {
@@ -502,13 +518,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                }
        }
 
-
-       /*
-        *      Register the counter comparison operation.
-        * FIXME: move all attributes to DA
-        */
-       paircompare_register(inst->dict_attr, NULL, true, counter_cmp, inst);
-
        /*
         * Init the mutex
         */
@@ -532,7 +541,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        int acctstatustype = 0;
        time_t diff;
 
-       if ((key_vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL)
+       if ((key_vp = fr_pair_find_by_num(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");
@@ -542,7 +551,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
                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);
+       uniqueid_vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID, 0, TAG_ANY);
        if (uniqueid_vp != NULL)
                DEBUG("rlm_counter: Packet Unique ID = '%s'",uniqueid_vp->vp_strvalue);
 
@@ -564,7 +573,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
         * 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) {
+               if ((proto_vp = fr_pair_find_by_num(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;
                }
@@ -577,7 +586,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
         * Check if request->timestamp - {Acct-Delay-Time} < last_reset
         * If yes reject the packet since it is very old
         */
-       key_vp = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
+       key_vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
        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");
@@ -592,7 +601,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
         *      The REAL username, after stripping.
         */
        key_vp = (inst->key_attr->attr == PW_USER_NAME) ? request->username :
-                                       pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
+                                       fr_pair_find_by_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;
@@ -601,7 +610,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        /*
         *      Look for the attribute to use as a counter.
         */
-       count_vp = pair_find_by_da(request->packet->vps, inst->count_attr, TAG_ANY);
+       count_vp = fr_pair_find_by_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;
@@ -722,7 +731,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         */
        DEBUG2("rlm_counter: Entering module authorize code");
        key_vp = (inst->key_attr->attr == PW_USER_NAME) ? request->username :
-                pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
+                fr_pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
        if (!key_vp) {
                DEBUG2("rlm_counter: Could not find Key value pair");
                return rcode;
@@ -731,7 +740,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        /*
         *      Look for the check item
         */
-       if ((check_vp = pair_find_by_da(request->config_items, inst->check_attr, TAG_ANY)) == NULL) {
+       if ((check_vp = fr_pair_find_by_da(request->config, inst->check_attr, TAG_ANY)) == NULL) {
                DEBUG2("rlm_counter: Could not find Check item value pair");
                return rcode;
        }
@@ -798,23 +807,23 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                                res += check_vp->vp_integer;
                        }
 
-                       reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
+                       reply_item = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
                        if (reply_item) {
                                if (reply_item->vp_integer > res) {
                                        reply_item->vp_integer = res;
                                }
                        } else {
-                               reply_item = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
+                               reply_item = radius_pair_create(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
                                reply_item->vp_integer = res;
                        }
                } else if (inst->reply_attr) {
-                       reply_item = pair_find_by_da(request->reply->vps, inst->reply_attr, TAG_ANY);
+                       reply_item = fr_pair_find_by_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->reply, &request->reply->vps, inst->reply_attr->attr,
+                               reply_item = radius_pair_create(request->reply, &request->reply->vps, inst->reply_attr->attr,
                                                               inst->reply_attr->vendor);
                                reply_item->vp_integer = res;
                        }
@@ -831,7 +840,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                 * User is denied access, send back a reply message
                */
                sprintf(msg, "Your maximum %s usage time has been reached", inst->reset);
-               pairmake_reply("Reply-Message", msg, T_OP_EQ);
+               pair_make_reply("Reply-Message", msg, T_OP_EQ);
 
                REDEBUG("Maximum %s usage time reached", inst->reset);
                rcode = RLM_MODULE_REJECT;
@@ -867,21 +876,16 @@ static int mod_detach(void *instance)
  */
 extern module_t rlm_counter;
 module_t rlm_counter = {
-       RLM_MODULE_INIT,
-       "counter",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_counter_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 */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "counter",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_counter_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_ACCOUNTING]        = mod_accounting
        },
 };
index cab139b..c7a667a 100644 (file)
@@ -104,7 +104,7 @@ static void calc_sha1_digest(uint8_t *buffer, uint8_t const *challenge,
 {
        uint8_t buf[1024];
        int i;
-       fr_SHA1_CTX context;
+       fr_sha1_ctx context;
 
        memset(buf, 0, 1024);
        memset(buf, 0x36, 64);
@@ -126,22 +126,22 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void * instance, REQ
        VALUE_PAIR *authtype, *challenge, *response, *password;
        uint8_t buffer[64];
 
-       password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+       password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
        if(!password) {
                AUTH("rlm_cram: Cleartext-Password is required for authentication");
                return RLM_MODULE_INVALID;
        }
-       authtype = pairfind(request->packet->vps, SM_AUTHTYPE, VENDORPEC_SM, TAG_ANY);
+       authtype = fr_pair_find_by_num(request->packet->vps, SM_AUTHTYPE, VENDORPEC_SM, TAG_ANY);
        if(!authtype) {
                AUTH("rlm_cram: Required attribute Sandy-Mail-Authtype missed");
                return RLM_MODULE_INVALID;
        }
-       challenge = pairfind(request->packet->vps, SM_CHALLENGE, VENDORPEC_SM, TAG_ANY);
+       challenge = fr_pair_find_by_num(request->packet->vps, SM_CHALLENGE, VENDORPEC_SM, TAG_ANY);
        if(!challenge) {
                AUTH("rlm_cram: Required attribute Sandy-Mail-Challenge missed");
                return RLM_MODULE_INVALID;
        }
-       response = pairfind(request->packet->vps, SM_RESPONSE, VENDORPEC_SM, TAG_ANY);
+       response = fr_pair_find_by_num(request->packet->vps, SM_RESPONSE, VENDORPEC_SM, TAG_ANY);
        if(!response) {
                AUTH("rlm_cram: Required attribute Sandy-Mail-Response missed");
                return RLM_MODULE_INVALID;
@@ -189,21 +189,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void * instance, REQ
 
 extern module_t rlm_cram;
 module_t rlm_cram = {
-       RLM_MODULE_INIT,
-       "CRAM",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       0,
-       NULL,                           /* CONF_PARSER */
-       NULL,                           /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authenticate */
-               NULL,                   /* authorize */
-               NULL,                   /* pre-accounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "cram",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate
        },
 };
index a82bff5..bff4053 100644 (file)
@@ -36,7 +36,7 @@ typedef struct rlm_date_t {
 
 static const CONF_PARSER module_config[] = {
        { "format", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_date_t, fmt), "%b %e %Y %H:%M:%S %Z" },
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 DIAG_OFF(format-nonliteral)
@@ -101,7 +101,7 @@ static ssize_t xlat_date_convert(void *instance, REQUEST *request, char const *f
 }
 DIAG_ON(format-nonliteral)
 
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        rlm_date_t *inst = instance;
 
@@ -117,18 +117,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
 extern module_t rlm_date;
 module_t rlm_date = {
-       RLM_MODULE_INIT,
-       "date",                         /* Name */
-       0,      /* type */
-       sizeof(rlm_date_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* pre-accounting */
-               NULL                    /* accounting */
-       },
+       .magic          = RLM_MODULE_INIT,
+       .name           = "date",
+       .inst_size      = sizeof(rlm_date_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap
 };
 
index cd7079d..6442238 100644 (file)
@@ -64,24 +64,24 @@ typedef struct detail_instance {
 
        bool            escape;         //!< do filename escaping, yes / no
 
-       RADIUS_ESCAPE_STRING escape_func; //!< escape function
+       xlat_escape_t escape_func; //!< escape function
 
        exfile_t        *ef;            //!< Log file handler
 
        fr_hash_table_t *ht;            //!< Holds suppressed attributes.
-} detail_instance_t;
+} rlm_detail_t;
 
 static const CONF_PARSER module_config[] = {
-       { "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 | PW_TYPE_XLAT, detail_instance_t, filename), "%A/%{Client-IP-Address}/detail" },
-       { "header", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, 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" },
-       { "escape_filenames", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, detail_instance_t, escape), "no" },
-       { "log_packet_header", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, detail_instance_t, log_srcdst), "no" },
-       { NULL, -1, 0, NULL, NULL }
+       { "detailfile", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED, rlm_detail_t, filename), NULL },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_detail_t, filename), "%A/%{Client-IP-Address}/detail" },
+       { "header", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_detail_t, header), "%t" },
+       { "detailperm", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_detail_t, perm), NULL },
+       { "permissions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_detail_t, perm), "0600" },
+       { "group", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_detail_t, group), NULL },
+       { "locking", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_detail_t, locking), "no" },
+       { "escape_filenames", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_detail_t, escape), "no" },
+       { "log_packet_header", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_detail_t, log_srcdst), "no" },
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -90,7 +90,7 @@ static const CONF_PARSER module_config[] = {
  */
 static int mod_detach(void *instance)
 {
-       detail_instance_t *inst = instance;
+       rlm_detail_t *inst = instance;
        if (inst->ht) fr_hash_table_free(inst->ht);
        return 0;
 }
@@ -111,86 +111,11 @@ static int detail_cmp(void const *a, void const *b)
 }
 
 /*
- *     Ensure that the filename doesn't walk back up the tree.
- */
-static size_t fix_directories(UNUSED REQUEST *request, char *out, size_t outlen,
-                             char const *in, UNUSED void *arg)
-{
-       char const *q = in;
-       char *p = out;
-       size_t left = outlen;
-
-       while (*q) {
-               if (*q != '/') {
-                       if (left < 2) break;
-
-                       /*
-                        *      Smash control characters and spaces to
-                        *      something simpler.
-                        */
-                       if (*q < ' ') {
-                               *(p++) = '_';
-                               continue;
-                       }
-
-                       *(p++) = *(q++);
-                       left--;
-                       continue;
-               }
-
-               /*
-                *      For now, allow slashes in the expanded
-                *      filename.  This allows the admin to set
-                *      attributes which create sub-directories.
-                *      Unfortunately, it also allows users to send
-                *      attributes which *may* end up creating
-                *      sub-directories.
-                */
-               if (left < 2) break;
-               *(p++) = *(q++);
-
-               /*
-                *      Get rid of ////../.././///.///..//
-                */
-       redo:
-               /*
-                *      Get rid of ////
-                */
-               if (*q == '/') {
-                       q++;
-                       goto redo;
-               }
-
-               /*
-                *      Get rid of /./././
-                */
-               if ((q[0] == '.') &&
-                   (q[1] == '/')) {
-                       q += 2;
-                       goto redo;
-               }
-
-               /*
-                *      Get rid of /../../../
-                */
-               if ((q[0] == '.') && (q[1] == '.') &&
-                   (q[2] == '/')) {
-                       q += 3;
-                       goto redo;
-               }
-       }
-       *p = '\0';
-
-       return (p - out);
-}
-
-
-/*
  *     (Re-)read radiusd.conf into memory.
  */
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
-       detail_instance_t *inst = instance;
+       rlm_detail_t *inst = instance;
        CONF_SECTION    *cs;
 
        inst->name = cf_section_name2(conf);
@@ -204,10 +129,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        if (inst->escape) {
                inst->escape_func = rad_filename_escape;
        } else {
-               inst->escape_func = fix_directories;
+               inst->escape_func = rad_filename_make_safe;
        }
 
-       inst->ef = exfile_init(inst, 64, 30);
+       inst->ef = exfile_init(inst, 64, 30, inst->locking);
        if (!inst->ef) {
                cf_log_err_cs(conf, "Failed creating log file context");
                return -1;
@@ -294,7 +219,7 @@ static void detail_vp_print(TALLOC_CTX *ctx, FILE *out, VALUE_PAIR const *stacke
  * @param[in] packet associated with the request (request, reply, proxy-request, proxy-reply...).
  * @param[in] compat Write out entry in compatibility mode.
  */
-static int detail_write(FILE *out, detail_instance_t *inst, REQUEST *request, RADIUS_PACKET *packet, bool compat)
+static int detail_write(FILE *out, rlm_detail_t *inst, REQUEST *request, RADIUS_PACKET *packet, bool compat)
 {
        VALUE_PAIR *vp;
        char timestamp[256];
@@ -428,7 +353,7 @@ static rlm_rcode_t CC_HINT(nonnull) detail_do(void *instance, REQUEST *request,
        char            *endptr;
 #endif
 
-       detail_instance_t *inst = instance;
+       rlm_detail_t *inst = instance;
 
        /*
         *      Generate the path for the detail file.  Use the same
@@ -511,7 +436,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
 {
 #ifdef WITH_DETAIL
        if (request->listener->type == RAD_LISTEN_DETAIL &&
-           strcmp(((detail_instance_t *)instance)->filename,
+           strcmp(((rlm_detail_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");
                return RLM_MODULE_NOOP;
@@ -602,29 +527,25 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *instance, REQUEST *requ
 /* globally exported name */
 extern module_t rlm_detail;
 module_t rlm_detail = {
-       RLM_MODULE_INIT,
-       "detail",
-       RLM_TYPE_HUP_SAFE,
-       sizeof(detail_instance_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               mod_accounting,         /* accounting */
-               NULL,                   /* checksimul */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "detail",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_detail_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_accounting,
+               [MOD_ACCOUNTING]        = mod_accounting,
 #ifdef WITH_PROXY
-               mod_pre_proxy,          /* pre-proxy */
-               mod_post_proxy,         /* post-proxy */
-#else
-               NULL, NULL,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
+               [MOD_POST_PROXY]        = mod_post_proxy,
 #endif
-               mod_post_auth           /* post-auth */
+               [MOD_POST_AUTH]         = mod_post_auth,
 #ifdef WITH_COA
-               , mod_recv_coa,
-               mod_send_coa
+               [MOD_RECV_COA]          = mod_recv_coa,
+               [MOD_SEND_COA]          = mod_send_coa
 #endif
        },
 };
index d3c81c8..22a29b8 100644 (file)
@@ -26,6 +26,7 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
+#include <freeradius-devel/md5.h>
 
 static int digest_fix(REQUEST *request)
 {
@@ -35,7 +36,7 @@ static int digest_fix(REQUEST *request)
        /*
         *      We need both of these attributes to do the authentication.
         */
-       first = pairfind(request->packet->vps, PW_DIGEST_RESPONSE, 0, TAG_ANY);
+       first = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_RESPONSE, 0, TAG_ANY);
        if (!first) {
                return RLM_MODULE_NOOP;
        }
@@ -52,7 +53,7 @@ static int digest_fix(REQUEST *request)
         */
        RDEBUG("Checking for correctly formatted Digest-Attributes");
 
-       first = pairfind(request->packet->vps, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY);
+       first = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY);
        if (!first) {
                return RLM_MODULE_NOOP;
        }
@@ -146,14 +147,14 @@ static int digest_fix(REQUEST *request)
                         *
                         *      Didn't they know that VSA's exist?
                         */
-                       sub = radius_paircreate(request->packet, &request->packet->vps,
+                       sub = radius_pair_create(request->packet, &request->packet->vps,
                                                PW_DIGEST_REALM - 1 + p[0], 0);
                        sub->vp_length = attrlen - 2;
                        sub->vp_strvalue = q = talloc_array(sub, char, sub->vp_length + 1);
                        memcpy(q, p + 2, attrlen - 2);
                        q[attrlen - 2] = '\0';
 
-                       if ((debug_flag > 1) && fr_log_fp) {
+                       if ((rad_debug_lvl > 1) && fr_log_fp) {
                                vp_print(fr_log_fp, sub);
                        }
 
@@ -181,7 +182,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
        if (rcode != RLM_MODULE_OK) return rcode;
 
 
-       if (pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY)) {
+       if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY)) {
                RWDEBUG2("Auth-Type already set.  Not setting to DIGEST");
                return RLM_MODULE_NOOP;
        }
@@ -190,7 +191,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
         *      Everything's OK, add a digest authentication type.
         */
        RDEBUG("Adding Auth-Type = DIGEST");
-       pairmake_config("Auth-Type", "DIGEST", T_OP_EQ);
+       pair_make_config("Auth-Type", "DIGEST", T_OP_EQ);
 
        return RLM_MODULE_OK;
 }
@@ -213,14 +214,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
         *      We require access to the plain-text password, or to the
         *      Digest-HA1 parameter.
         */
-       passwd = pairfind(request->config_items, PW_DIGEST_HA1, 0, TAG_ANY);
+       passwd = fr_pair_find_by_num(request->config, PW_DIGEST_HA1, 0, TAG_ANY);
        if (passwd) {
                if (passwd->vp_length != 32) {
                        RAUTH("Digest-HA1 has invalid length, authentication failed");
                        return RLM_MODULE_INVALID;
                }
        } else {
-               passwd = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+               passwd = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
        }
        if (!passwd) {
                RAUTH("Cleartext-Password or Digest-HA1 is required for authentication");
@@ -230,7 +231,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        /*
         *      We need these, too.
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY);
        if (!vp) {
        error:
                REDEBUG("You set 'Auth-Type = Digest' for a request that does not contain any digest attributes!");
@@ -244,7 +245,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
         *      "authorize" section.  In that case, try to decode the
         *      attributes here.
         */
-       if (!pairfind(request->packet->vps, PW_DIGEST_NONCE, 0, TAG_ANY)) {
+       if (!fr_pair_find_by_num(request->packet->vps, PW_DIGEST_NONCE, 0, TAG_ANY)) {
                int rcode;
 
                rcode = digest_fix(request);
@@ -261,7 +262,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        /*
         *      We require access to the Digest-Nonce-Value
         */
-       nonce = pairfind(request->packet->vps, PW_DIGEST_NONCE, 0, TAG_ANY);
+       nonce = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_NONCE, 0, TAG_ANY);
        if (!nonce) {
                REDEBUG("No Digest-Nonce: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -270,7 +271,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        /*
         *      A1 = Digest-User-Name ":" Realm ":" Password
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_USER_NAME, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_USER_NAME, 0, TAG_ANY);
        if (!vp) {
                REDEBUG("No Digest-User-Name: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -281,7 +282,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        a1[a1_len] = ':';
        a1_len++;
 
-       vp = pairfind(request->packet->vps, PW_DIGEST_REALM, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_REALM, 0, TAG_ANY);
        if (!vp) {
                REDEBUG("No Digest-Realm: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -307,7 +308,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
         *      See which variant we calculate.
         *      Assume MD5 if no Digest-Algorithm attribute received
         */
-       algo = pairfind(request->packet->vps, PW_DIGEST_ALGORITHM, 0, TAG_ANY);
+       algo = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_ALGORITHM, 0, TAG_ANY);
        if ((!algo) ||
            (strcasecmp(algo->vp_strvalue, "MD5") == 0)) {
                /*
@@ -351,7 +352,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                a1[a1_len] = ':';
                a1_len++;
 
-               vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_CNONCE, 0, TAG_ANY);
                if (!vp) {
                        REDEBUG("No Digest-CNonce: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
@@ -379,7 +380,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        /*
         *      A2 = Digest-Method ":" Digest-URI
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_METHOD, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_METHOD, 0, TAG_ANY);
        if (!vp) {
                REDEBUG("No Digest-Method: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -390,7 +391,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        a2[a2_len] = ':';
        a2_len++;
 
-       vp = pairfind(request->packet->vps, PW_DIGEST_URI, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_URI, 0, TAG_ANY);
        if (!vp) {
                REDEBUG("No Digest-URI: Cannot perform Digest authentication");
                return RLM_MODULE_INVALID;
@@ -401,7 +402,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        /*
         *  QOP is "auth-int", tack on ": Digest-Body-Digest"
         */
-       qop = pairfind(request->packet->vps, PW_DIGEST_QOP, 0, TAG_ANY);
+       qop = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_QOP, 0, TAG_ANY);
        if (qop) {
                if (strcasecmp(qop->vp_strvalue, "auth-int") == 0) {
                        VALUE_PAIR *body;
@@ -415,7 +416,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                        /*
                         *  Must be a hex representation of an MD5 digest.
                         */
-                       body = pairfind(request->packet->vps, PW_DIGEST_BODY_DIGEST, 0, TAG_ANY);
+                       body = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_BODY_DIGEST, 0, TAG_ANY);
                        if (!body) {
                                REDEBUG("No Digest-Body-Digest: Cannot perform Digest authentication");
                                return RLM_MODULE_INVALID;
@@ -454,7 +455,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        fr_bin2hex((char *) kd, hash, sizeof(hash));
 
 #ifndef NRDEBUG
-       if (debug_flag > 1) {
+       if (rad_debug_lvl > 1) {
                fr_printf_log("H(A1) = ");
                for (i = 0; i < 16; i++) {
                        fr_printf_log("%02x", hash[i]);
@@ -486,7 +487,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                kd[kd_len] = ':';
                kd_len++;
 
-               vp = pairfind(request->packet->vps, PW_DIGEST_NONCE_COUNT, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_NONCE_COUNT, 0, TAG_ANY);
                if (!vp) {
                        REDEBUG("No Digest-Nonce-Count: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
@@ -497,7 +498,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
                kd[kd_len] = ':';
                kd_len++;
 
-               vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_CNONCE, 0, TAG_ANY);
                if (!vp) {
                        REDEBUG("No Digest-CNonce: Cannot perform Digest authentication");
                        return RLM_MODULE_INVALID;
@@ -523,7 +524,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        fr_bin2hex((char *) kd + kd_len, hash, sizeof(hash));
 
 #ifndef NRDEBUG
-       if (debug_flag > 1) {
+       if (rad_debug_lvl > 1) {
                fr_printf_log("H(A2) = ");
                for (i = 0; i < 16; i++) {
                        fr_printf_log("%02x", hash[i]);
@@ -546,7 +547,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        /*
         *      Get the binary value of Digest-Response
         */
-       vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_DIGEST_RESPONSE, 0, TAG_ANY);
        if (!vp) {
                REDEBUG("No Digest-Response attribute in the request.  Cannot perform digest authentication");
                return RLM_MODULE_INVALID;
@@ -558,7 +559,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
        }
 
 #ifndef NRDEBUG
-       if (debug_flag > 1) {
+       if (rad_debug_lvl > 1) {
                fr_printf_log("EXPECTED ");
                for (i = 0; i < 16; i++) {
                        fr_printf_log("%02x", kd[i]);
@@ -595,21 +596,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU
  */
 extern module_t rlm_digest;
 module_t rlm_digest = {
-       RLM_MODULE_INIT,
-       "digest",
-       0,      /* type */
-       0,
-       NULL,                           /* CONF_PARSER */
-       NULL,                           /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,  /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "digest",
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
index 5ef1070..5f230aa 100644 (file)
@@ -110,21 +110,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
  */
 extern module_t rlm_dynamic_clients;
 module_t rlm_dynamic_clients = {
-       RLM_MODULE_INIT,
-       "dynamic_clients",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       0,
-       NULL,                           /* CONF_PARSER */
-       NULL,                           /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,  /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "dynamic_clients",
+       .type           = RLM_TYPE_THREAD_SAFE,         /* type */
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
index 90bf721..6b979cc 100644 (file)
@@ -93,7 +93,7 @@ static int _eap_module_free(eap_module_t *inst)
 /** Load required EAP sub-modules (methods)
  *
  */
-int eap_module_load(rlm_eap_t *inst, eap_module_t **m_inst, eap_type_t num, CONF_SECTION *cs)
+int eap_module_instantiate(rlm_eap_t *inst, eap_module_t **m_inst, eap_type_t num, CONF_SECTION *cs)
 {
        eap_module_t *method;
        char *mod_name, *p;
@@ -132,7 +132,7 @@ int eap_module_load(rlm_eap_t *inst, eap_module_t **m_inst, eap_type_t num, CONF
         */
        method->handle = lt_dlopenext(mod_name);
        if (!method->handle) {
-               ERROR("rlm_eap (%s): Failed to link %s: %s", inst->xlat_name, mod_name, lt_dlerror());
+               ERROR("rlm_eap (%s): Failed to link %s: %s", inst->xlat_name, mod_name, fr_strerror());
 
                return -1;
        }
@@ -153,7 +153,7 @@ open_self:
        /*
         *      Call the attach num in the EAP num module
         */
-       if ((method->type->attach) && ((method->type->attach)(method->cs, &(method->instance)) < 0)) {
+       if ((method->type->instantiate) && ((method->type->instantiate)(method->cs, &(method->instance)) < 0)) {
                ERROR("rlm_eap (%s): Failed to initialise %s", inst->xlat_name, mod_name);
 
                if (method->instance) {
@@ -182,35 +182,24 @@ static int eap_module_call(eap_module_t *module, eap_handler_t *handler)
 
        rad_assert(module != NULL);
 
-       RDEBUG2("Calling %s to process EAP data", module->type->name);
+       RDEBUG2("Calling submodule %s to process data", module->type->name);
 
        request->module = module->type->name;
 
        switch (handler->stage) {
        case INITIATE:
-               if (!module->type->initiate(module->instance, handler)) {
+               if (!module->type->session_init(module->instance, handler)) {
                        rcode = 0;
                }
 
                break;
 
-       case AUTHORIZE:
+       case PROCESS:
                /*
                 *   The called function updates the EAP reply packet.
                 */
-               if (!module->type->authorize ||
-                   !module->type->authorize(module->instance, handler)) {
-                       rcode = 0;
-               }
-
-               break;
-
-       case AUTHENTICATE:
-               /*
-                *   The called function updates the EAP reply packet.
-                */
-               if (!module->type->authenticate ||
-                   !module->type->authenticate(module->instance, handler)) {
+               if (!module->type->process ||
+                   !module->type->process(module->instance, handler)) {
                        rcode = 0;
                }
 
@@ -218,7 +207,7 @@ static int eap_module_call(eap_module_t *module, eap_handler_t *handler)
 
        default:
                /* Should never enter here */
-               RDEBUG("Internal sanity check failed on eap");
+               RDEBUG("Internal sanity check failed on EAP");
                rcode = 0;
                break;
        }
@@ -257,7 +246,7 @@ static eap_type_t eap_process_nak(rlm_eap_t *inst, REQUEST *request,
         *      Pick one type out of the one they asked for,
         *      as they may have asked for many.
         */
-       vp = pairfind(request->config_items, PW_EAP_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_EAP_TYPE, 0, TAG_ANY);
        for (i = 0; i < nak->length; i++) {
                /*
                 *      Type 0 is valid, and means there are no
@@ -361,7 +350,7 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
         *      Don't trust anyone.
         */
        if ((type->num == 0) || (type->num >= PW_EAP_MAX_TYPES)) {
-               REDEBUG("Peer sent method %d, which is outside known range", type->num);
+               REDEBUG("Peer sent EAP method number %d, which is outside known range", type->num);
 
                return EAP_INVALID;
        }
@@ -375,7 +364,7 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
                return EAP_INVALID;
        }
 
-       RDEBUG2("Peer sent method %s (%d)", eap_type2name(type->num), type->num);
+       RDEBUG2("Peer sent packet with method EAP %s (%d)", eap_type2name(type->num), type->num);
        /*
         *      Figure out what to do.
         */
@@ -384,7 +373,7 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
                /*
                 *      Allow per-user configuration of EAP types.
                 */
-               vp = pairfind(handler->request->config_items, PW_EAP_TYPE, 0,
+               vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TYPE, 0,
                              TAG_ANY);
                if (vp) next = vp->vp_integer;
 
@@ -394,8 +383,7 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
                if ((next < PW_EAP_MD5) ||
                    (next >= PW_EAP_MAX_TYPES) ||
                    (!inst->methods[next])) {
-                       REDEBUG2("Tried to start unsupported method (%d)",
-                                next);
+                       REDEBUG2("Tried to start unsupported method (%d)", next);
 
                        return EAP_INVALID;
                }
@@ -412,10 +400,8 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
                handler->type = next;
 
                if (eap_module_call(inst->methods[next], handler) == 0) {
-                       REDEBUG2("Failed starting EAP %s (%d) session. "
-                                "EAP sub-module failed",
-                                eap_type2name(next),
-                                next);
+                       REDEBUG2("Failed starting EAP %s (%d) session.  EAP sub-module failed",
+                                eap_type2name(next), next);
 
                        return EAP_INVALID;
                }
@@ -447,28 +433,22 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
                 *      Key off of the configured sub-modules.
                 */
                default:
-                       RDEBUG2("EAP %s (%d)",
-                               eap_type2name(type->num),
-                               type->num);
-
                        /*
                         *      We haven't configured it, it doesn't exit.
                         */
                        if (!inst->methods[type->num]) {
-                               REDEBUG2("Client asked for unsupported "
-                                        "type %s (%d)",
+                               REDEBUG2("Client asked for unsupported method %s (%d)",
                                         eap_type2name(type->num),
                                         type->num);
 
                                return EAP_INVALID;
                        }
 
-                       rad_assert(handler->stage == AUTHENTICATE);
+                       rad_assert(handler->stage == PROCESS);
                        handler->type = type->num;
                        if (eap_module_call(inst->methods[type->num],
                                            handler) == 0) {
-                               REDEBUG2("Failed continuing EAP %s (%d) session. "
-                                        "EAP sub-module failed",
+                               REDEBUG2("Failed continuing EAP %s (%d) session.  EAP sub-module failed",
                                         eap_type2name(type->num),
                                         type->num);
 
@@ -552,9 +532,6 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
                default:
                        ++reply->id;
                }
-       } else {
-               RDEBUG2("Underlying EAP-Type set EAP ID to %d",
-                      reply->id);
        }
 
        /*
@@ -579,7 +556,7 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
        }
        eap_packet = (eap_packet_raw_t *)reply->packet;
 
-       vp = radius_paircreate(request->reply, &request->reply->vps, PW_EAP_MESSAGE, 0);
+       vp = radius_pair_create(request->reply, &request->reply->vps, PW_EAP_MESSAGE, 0);
        if (!vp) return RLM_MODULE_INVALID;
 
        vp->vp_length = eap_packet->length[0] * 256 + eap_packet->length[1];
@@ -593,12 +570,12 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
         *      Don't add a Message-Authenticator if it's already
         *      there.
         */
-       vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
        if (!vp) {
-               vp = paircreate(request->reply, PW_MESSAGE_AUTHENTICATOR, 0);
+               vp = fr_pair_afrom_num(request->reply, PW_MESSAGE_AUTHENTICATOR, 0);
                vp->vp_length = AUTH_VECTOR_LEN;
                vp->vp_octets = talloc_zero_array(vp, uint8_t, vp->vp_length);
-               pairadd(&(request->reply->vps), vp);
+               fr_pair_add(&(request->reply->vps), vp);
        }
 
        /* Set request reply code, but only if it's not already set. */
@@ -631,13 +608,17 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
                }
 
                /* Should never enter here */
-               ERROR("rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
+               REDEBUG("Reply code %d is unknown, rejecting the request", reply->code);
                request->reply->code = PW_CODE_ACCESS_REJECT;
                reply->code = PW_EAP_FAILURE;
                rcode = RLM_MODULE_REJECT;
                break;
        }
 
+       RDEBUG2("Sending EAP %s (code %i) ID %d length %i",
+               eap_codes[eap_packet->code], eap_packet->code, reply->id,
+               eap_packet->length[0] * 256 + eap_packet->length[1]);
+
        return rcode;
 }
 
@@ -650,7 +631,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
        VALUE_PAIR *vp, *proxy;
        VALUE_PAIR *eap_msg;
 
-       eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+       eap_msg = fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
        if (!eap_msg) {
                RDEBUG2("No EAP-Message, not doing EAP");
                return EAP_NOOP;
@@ -660,7 +641,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
         *      Look for EAP-Type = None (FreeRADIUS specific attribute)
         *      this allows you to NOT do EAP for some users.
         */
-       vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(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");
                return EAP_NOOP;
@@ -676,7 +657,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
         *      Check for a Proxy-To-Realm.  Don't get excited over LOCAL
         *      realms (sigh).
         */
-       proxy = pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY);
+       proxy = fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
        if (proxy) {
                REALM *realm;
 
@@ -715,9 +696,9 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
                }
 
                RDEBUG2("Got EAP_START message");
-               vp = paircreate(request->reply, PW_EAP_MESSAGE, 0);
+               vp = fr_pair_afrom_num(request->reply, PW_EAP_MESSAGE, 0);
                if (!vp) return EAP_FAIL;
-               pairadd(&request->reply->vps, vp);
+               fr_pair_add(&request->reply->vps, vp);
 
                /*
                 *      Manually create an EAP Identity request
@@ -750,10 +731,10 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
         *      Create an EAP-Type containing the EAP-type
         *      from the packet.
         */
-       vp = paircreate(request->packet, PW_EAP_TYPE, 0);
+       vp = fr_pair_afrom_num(request->packet, PW_EAP_TYPE, 0);
        if (vp) {
                vp->vp_integer = eap_msg->vp_octets[4];
-               pairadd(&(request->packet->vps), vp);
+               fr_pair_add(&(request->packet->vps), vp);
        }
 
        /*
@@ -776,9 +757,9 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
         */
        if ((eap_msg->vp_octets[0] == 0) ||
            (eap_msg->vp_octets[0] >= PW_EAP_MAX_CODES)) {
-               RDEBUG2("Unknown EAP packet");
+               RDEBUG2("Peer sent EAP packet with unknown code %i", eap_msg->vp_octets[0]);
        } else {
-               RDEBUG2("Peer sent code %s (%i) ID %d length %zu",
+               RDEBUG2("Peer sent EAP %s (code %i) ID %d length %zu",
                        eap_codes[eap_msg->vp_octets[0]],
                        eap_msg->vp_octets[0],
                        eap_msg->vp_octets[1],
@@ -881,12 +862,13 @@ void eap_fail(eap_handler_t *handler)
        /*
         *      Delete any previous replies.
         */
-       pairdelete(&handler->request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
-       pairdelete(&handler->request->reply->vps, PW_STATE, 0, TAG_ANY);
+       fr_pair_delete_by_num(&handler->request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+       fr_pair_delete_by_num(&handler->request->reply->vps, PW_STATE, 0, TAG_ANY);
 
        talloc_free(handler->eap_ds->request);
        handler->eap_ds->request = talloc_zero(handler->eap_ds, eap_packet_t);
        handler->eap_ds->request->code = PW_EAP_FAILURE;
+       handler->finished = true;
        eap_compose(handler);
 }
 
@@ -896,15 +878,17 @@ void eap_fail(eap_handler_t *handler)
 void eap_success(eap_handler_t *handler)
 {
        handler->eap_ds->request->code = PW_EAP_SUCCESS;
+       handler->finished = true;
        eap_compose(handler);
 }
 
 /*
  * Basic EAP packet verfications & validations
  */
-static int eap_validation(REQUEST *request, eap_packet_raw_t *eap_packet)
+static int eap_validation(REQUEST *request, eap_packet_raw_t **eap_packet_p)
 {
        uint16_t len;
+       eap_packet_raw_t *eap_packet = *eap_packet_p;
 
        memcpy(&len, eap_packet->length, sizeof(uint16_t));
        len = ntohs(len);
@@ -914,11 +898,74 @@ static int eap_validation(REQUEST *request, eap_packet_raw_t *eap_packet)
         */
        if ((len <= EAP_HEADER_LEN) ||
            ((eap_packet->code != PW_EAP_RESPONSE) &&
-            (eap_packet->code != PW_EAP_REQUEST)) ||
-           (eap_packet->data[0] <= 0) ||
+            (eap_packet->code != PW_EAP_REQUEST))) {
+               RAUTH("Badly formatted EAP Message: Ignoring the packet");
+               return EAP_INVALID;
+       }
+
+       if ((eap_packet->data[0] <= 0) ||
            (eap_packet->data[0] >= PW_EAP_MAX_TYPES)) {
+               /*
+                *      Handle expanded types by smashing them to
+                *      normal types.
+                */
+               if (eap_packet->data[0] == PW_EAP_EXPANDED_TYPE) {
+                       uint8_t *p, *q;
 
-               RAUTH("Badly formatted EAP Message: Ignoring the packet");
+                       if (len <= (EAP_HEADER_LEN + 1 + 3 + 4)) {
+                               RAUTH("Expanded EAP type is too short: ignoring the packet");
+                               return EAP_INVALID;
+                       }
+
+                       if ((eap_packet->data[1] != 0) ||
+                           (eap_packet->data[2] != 0) ||
+                           (eap_packet->data[3] != 0)) {
+                               RAUTH("Expanded EAP type has unknown Vendor-ID: ignoring the packet");
+                               return EAP_INVALID;
+                       }
+
+                       if ((eap_packet->data[4] != 0) ||
+                           (eap_packet->data[5] != 0) ||
+                           (eap_packet->data[6] != 0)) {
+                               RAUTH("Expanded EAP type has unknown Vendor-Type: ignoring the packet");
+                               return EAP_INVALID;
+                       }
+
+                       if ((eap_packet->data[7] == 0) ||
+                           (eap_packet->data[7] >= PW_EAP_MAX_TYPES)) {
+                               RAUTH("Unsupported Expanded EAP type %u: ignoring the packet", eap_packet->data[7]);
+                               return EAP_INVALID;
+                       }
+
+                       if (eap_packet->data[7] == PW_EAP_NAK) {
+                               RAUTH("Unsupported Expanded EAP-NAK: ignoring the packet");
+                               return EAP_INVALID;
+                       }
+
+                       /*
+                        *      Re-write the EAP packet to NOT have the expanded type.
+                        */
+                       q = (uint8_t *) eap_packet;
+                       memmove(q + EAP_HEADER_LEN, q + EAP_HEADER_LEN + 7, len - 7 - EAP_HEADER_LEN);
+
+                       p = talloc_realloc(talloc_parent(eap_packet), eap_packet, uint8_t, len - 7);
+                       if (!p) {
+                               RAUTH("Unsupported EAP type %u: ignoring the packet", eap_packet->data[0]);
+                               return EAP_INVALID;
+                       }
+
+                       len -= 7;
+                       p[2] = (len >> 8) & 0xff;
+                       p[3] = len & 0xff;
+
+                       *eap_packet_p = (eap_packet_raw_t *) p;
+                       RWARN("Converting Expanded EAP to normal EAP.");
+                       RWARN("Unnecessary use of Expanded EAP types is not recommened.");
+
+                       return EAP_VALID;
+               }
+
+               RAUTH("Unsupported EAP type %u: ignoring the packet", eap_packet->data[0]);
                return EAP_INVALID;
        }
 
@@ -1036,19 +1083,21 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p,
                           REQUEST *request)
 {
        eap_handler_t   *handler = NULL;
-       eap_packet_raw_t        *eap_packet = *eap_packet_p;
+       eap_packet_raw_t *eap_packet;
        VALUE_PAIR      *vp;
 
        /*
         *      Ensure it's a valid EAP-Request, or EAP-Response.
         */
-       if (eap_validation(request, eap_packet) == EAP_INVALID) {
+       if (eap_validation(request, eap_packet_p) == EAP_INVALID) {
        error:
                talloc_free(*eap_packet_p);
                *eap_packet_p = NULL;
                return NULL;
        }
 
+       eap_packet = *eap_packet_p;
+
        /*
         *      eap_handler_t MUST be found in the list if it is not
         *      EAP-Identity response
@@ -1079,7 +1128,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p,
                        goto error;
                }
 
-              vp = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+              vp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
               if (!vp) {
                       /*
                        *       NAS did not set the User-Name
@@ -1089,7 +1138,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p,
                        *       correctly
                        */
                       RDEBUG2("Broken NAS did not set User-Name, setting from EAP Identity");
-                      vp = pairmake(request->packet, &request->packet->vps, "User-Name", handler->identity, T_OP_EQ);
+                      vp = fr_pair_make(request->packet, &request->packet->vps, "User-Name", handler->identity, T_OP_EQ);
                       if (!vp) {
                               goto error;
                       }
@@ -1126,7 +1175,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p,
                        goto error;
                }
 
-              vp = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+              vp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
               if (!vp) {
                       /*
                        *       NAS did not set the User-Name
@@ -1136,7 +1185,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p,
                        *       correctly
                        */
                       RWDEBUG2("NAS did not set User-Name.  Setting it locally from EAP Identity");
-                      vp = pairmake(request->packet, &request->packet->vps, "User-Name", handler->identity, T_OP_EQ);
+                      vp = fr_pair_make(request->packet, &request->packet->vps, "User-Name", handler->identity, T_OP_EQ);
                       if (!vp) {
                               goto error2;
                       }
index 15eeafa..61d9474 100644 (file)
@@ -57,8 +57,7 @@ typedef struct eap_ds {
  */
 typedef enum operation_t {
        INITIATE = 0,
-       AUTHORIZE,
-       AUTHENTICATE
+       PROCESS
 } operation_t;
 
 
@@ -119,8 +118,8 @@ typedef struct _eap_handler {
 
        int             trips;
 
-       int             tls;
-       int             finished;
+       bool            tls;
+       bool            finished;
        VALUE_PAIR      *certs;
 } eap_handler_t;
 
@@ -128,12 +127,12 @@ typedef struct _eap_handler {
  * Interface to call EAP sub mdoules
  */
 typedef struct rlm_eap_module {
-       char const *name;
-       int (*attach)(CONF_SECTION *conf, void **instance);
-       int (*initiate)(void *instance, eap_handler_t *handler);
-       int (*authorize)(void *instance, eap_handler_t *handler);
-       int (*authenticate)(void *instance, eap_handler_t *handler);
-       int (*detach)(void *instance);
+       char const *name;                                               //!< The name of the sub-module
+                                                                       //!< (without rlm_ prefix).
+       int (*instantiate)(CONF_SECTION *conf, void **instance);        //!< Create a new submodule instance.
+       int (*session_init)(void *instance, eap_handler_t *handler);    //!< Initialise a new EAP session.
+       int (*process)(void *instance, eap_handler_t *handler);         //!< Continue an EAP session.
+       int (*detach)(void *instance);                                  //!< Destroy a submodule instance.
 } rlm_eap_module_t;
 
 #define REQUEST_DATA_EAP_HANDLER        (1)
index e4d6de0..8ad8564 100644 (file)
@@ -62,7 +62,7 @@ static bool chbind_build_response(REQUEST *request, CHBIND_REQ *chbind)
         *      Set the response code.  Default to "fail" if none was
         *      specified.
         */
-       vp = pairfind(request->config_items, PW_CHBIND_RESPONSE_CODE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_CHBIND_RESPONSE_CODE, 0, TAG_ANY);
        if (vp) {
                ptr[0] = vp->vp_integer;
        } else {
@@ -166,12 +166,12 @@ PW_CODE chbind_process(REQUEST *request, CHBIND_REQ *chbind)
 
        /* Set-up the fake request */
        fake = request_alloc_fake(request);
-       pairmake_packet("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
+       pair_make_request("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
 
        /* Add the username to the fake request */
        if (chbind->username) {
-               vp = paircopyvp(fake->packet, chbind->username);
-               pairadd(&fake->packet->vps, vp);
+               vp = fr_pair_copy(fake->packet, chbind->username);
+               fr_pair_add(&fake->packet->vps, vp);
                fake->username = vp;
        }
 
@@ -193,7 +193,7 @@ PW_CODE chbind_process(REQUEST *request, CHBIND_REQ *chbind)
                                return PW_CODE_ACCESS_ACCEPT;
                        }
                        if (vp) {
-                               pairadd(&fake->packet->vps, vp);
+                               fr_pair_add(&fake->packet->vps, vp);
                        }
                        attr_data += attr_len;
                        data_len -= attr_len;
@@ -242,7 +242,7 @@ chbind_packet_t *eap_chbind_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
        chbind_packet_t *packet;
        vp_cursor_t cursor;
 
-       first = pairfind(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY);
+       first = fr_pair_find_by_num(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY);
        if (!first) return NULL;
 
        /*
@@ -286,9 +286,9 @@ VALUE_PAIR *eap_chbind_packet2vp(REQUEST *request, chbind_packet_t *packet)
 
        if (!packet) return NULL; /* don't produce garbage */
 
-       vp = paircreate(request->packet, PW_UKERNA_CHBIND, VENDORPEC_UKERNA);
+       vp = fr_pair_afrom_num(request->packet, PW_UKERNA_CHBIND, VENDORPEC_UKERNA);
        if (!vp) return NULL;
-       pairmemcpy(vp, (uint8_t *) packet, talloc_array_length((uint8_t *)packet));
+       fr_pair_value_memcpy(vp, (uint8_t *) packet, talloc_array_length((uint8_t *)packet));
 
        return vp;
 }
index dae5012..2846e1d 100644 (file)
@@ -64,11 +64,9 @@ USES_APPLE_DEPRECATED_API    /* OpenSSL API has been deprecated by Apple */
 tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_conf, bool client_cert)
 {
        tls_session_t   *ssn;
-       int             verify_mode = 0;
        REQUEST         *request = handler->request;
 
        handler->tls = true;
-       handler->finished = false;
 
        /*
         *      Every new session is started only from EAP-TLS-START.
@@ -83,17 +81,6 @@ tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_
        }
 
        /*
-        *      Verify the peer certificate, if asked.
-        */
-       if (client_cert) {
-               RDEBUG2("Requiring client certificate");
-               verify_mode = SSL_VERIFY_PEER;
-               verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
-               verify_mode |= SSL_VERIFY_CLIENT_ONCE;
-       }
-       SSL_set_verify(ssn->ssl, verify_mode, cbtls_verify);
-
-       /*
         *      Create a structure for all the items required to be
         *      verified for each client and set that as opaque data
         *      structure.
@@ -236,8 +223,8 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
        reply.flags = ssn->peap_flag;
 
        /* Send data, NOT more than the FRAGMENT size */
-       if (ssn->dirty_out.used > ssn->offset) {
-               size = ssn->offset;
+       if (ssn->dirty_out.used > ssn->mtu) {
+               size = ssn->mtu;
                reply.flags = SET_MORE_FRAGMENTS(reply.flags);
                /* Length MUST be included if it is the First Fragment */
                if (ssn->fragment == 0) {
@@ -285,17 +272,19 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
  *     fragments to receive to make the complete
  *     TLS-record/TLS-Message
  */
-static int eaptls_send_ack(EAP_DS *eap_ds, int peap_flag)
+static int eaptls_send_ack(eap_handler_t *handler, int peap_flag)
 {
        EAPTLS_PACKET   reply;
+       REQUEST         *request = handler->request;
 
+       RDEBUG2("ACKing Peer's TLS record fragment");
        reply.code = FR_TLS_ACK;
        reply.length = TLS_HEADER_LEN + 1/*flags*/;
        reply.flags = peap_flag;
        reply.data = NULL;
        reply.dlen = 0;
 
-       eaptls_compose(eap_ds, &reply);
+       eaptls_compose(handler->eap_ds, &reply);
 
        return 1;
 }
@@ -311,10 +300,12 @@ static int eaptls_send_ack(EAP_DS *eap_ds, int peap_flag)
  */
 static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
 {
-       EAP_DS *eap_ds = handler->eap_ds;
-       EAP_DS *prev_eap_ds = handler->prev_eapds;
-       eaptls_packet_t *eaptls_packet, *eaptls_prev = NULL;
-       REQUEST *request = handler->request;
+       EAP_DS                  *eap_ds = handler->eap_ds;
+       tls_session_t           *tls_session = handler->opaque;
+       EAP_DS                  *prev_eap_ds = handler->prev_eapds;
+       eaptls_packet_t         *eaptls_packet, *eaptls_prev = NULL;
+       REQUEST                 *request = handler->request;
+       size_t                  frag_len;
 
        /*
         *      We don't check ANY of the input parameters.  It's all
@@ -335,6 +326,14 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
                eaptls_prev = (eaptls_packet_t *)prev_eap_ds->response->type.data;
 
        /*
+        *      First output the flags (for debugging)
+        */
+       RDEBUG3("Peer sent flags %c%c%c",
+               TLS_START(eaptls_packet->flags) ? 'S' : '-',
+               TLS_MORE_FRAGMENTS(eaptls_packet->flags) ? 'M' : '-',
+               TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 'L' : '-');
+
+       /*
         *      check for ACK
         *
         *      If there's no TLS data, or there's 1 byte of TLS data,
@@ -346,15 +345,10 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
            ((eap_ds->response->length == EAP_HEADER_LEN + 2) &&
             ((eaptls_packet->flags & 0xc0) == 0x00))) {
 
-               if (prev_eap_ds &&
-                   (prev_eap_ds->request->id == eap_ds->response->id)) {
-                       /*
-                        *      Run the ACK handler directly from here.
-                        */
-                       RDEBUG2("Received TLS ACK");
+               if (prev_eap_ds && (prev_eap_ds->request->id == eap_ds->response->id)) {
                        return tls_ack_handler(handler->opaque, request);
                } else {
-                       RERROR("Received Invalid TLS ACK");
+                       REDEBUG("Received Invalid TLS ACK");
                        return FR_TLS_INVALID;
                }
        }
@@ -363,11 +357,17 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
         *      We send TLS_START, but do not receive it.
         */
        if (TLS_START(eaptls_packet->flags)) {
-               REDEBUG("Received unexpected EAP-TLS Start message");
+               REDEBUG("Peer sent EAP-TLS Start message (only the server is allowed to do this)");
                return FR_TLS_INVALID;
        }
 
        /*
+        *      Calculate this fragment's length
+        */
+       frag_len = eap_ds->response->length -
+                  (EAP_HEADER_LEN + (TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 6 : 2));
+
+       /*
         *      The L bit (length included) is set to indicate the
         *      presence of the four octet TLS Message Length field,
         *      and MUST be set for the first fragment of a fragmented
@@ -381,41 +381,102 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
         *      from a fragment acknowledgement.
         */
        if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) {
-               RDEBUG2("TLS Length %d",
-                      eaptls_packet->data[2] * 256 | eaptls_packet->data[3]);
+               size_t total_len = eaptls_packet->data[2] * 256 | eaptls_packet->data[3];
+
+               if (frag_len > total_len) {
+                       RWDEBUG("TLS fragment length (%zu bytes) greater than TLS record length (%zu bytes)", frag_len,
+                               total_len);
+               }
+
+               RDEBUG2("Peer indicated complete TLS record size will be %zu bytes", total_len);
                if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
                        /*
-                        * FIRST_FRAGMENT is identified
-                        * 1. If there is no previous EAP-response received.
-                        * 2. If EAP-response received, then its M bit not set.
-                        *      (It is because Last fragment will not have M bit set)
+                        *      The supplicant is free to send fragments of wildly varying
+                        *      lengths, but the vast majority won't.
+                        *
+                        *      In this calculation we take into account the fact that the future
+                        *      fragments are likely to be 4 bytes larger than the initial one
+                        *      as they won't contain the length field.
+                        */
+                       if (frag_len + 4) {     /* check for wrap, else clang scan gets excited */
+                               RDEBUG2("Expecting %i TLS record fragments",
+                                       (int)((((total_len - frag_len) + ((frag_len + 4) - 1)) / (frag_len + 4)) + 1));
+                       }
+
+                       /*
+                        *      FIRST_FRAGMENT is identified
+                        *      1. If there is no previous EAP-response received.
+                        *      2. If EAP-response received, then its M bit not set.
+                        *         (It is because Last fragment will not have M bit set)
                         */
-                       if (!prev_eap_ds ||
-                           (!prev_eap_ds->response) ||
-                           (!eaptls_prev) ||
+                       if (!prev_eap_ds || (!prev_eap_ds->response) || (!eaptls_prev) ||
                            !TLS_MORE_FRAGMENTS(eaptls_prev->flags)) {
+                               RDEBUG2("Got first TLS record fragment (%zu bytes).  Peer indicated more fragments "
+                                       "to follow", frag_len);
+                               tls_session->tls_record_in_total_len = total_len;
+                               tls_session->tls_record_in_recvd_len = frag_len;
 
-                               RDEBUG2("Received EAP-TLS First Fragment of the message");
                                return FR_TLS_FIRST_FRAGMENT;
-                       } else {
+                       }
+
+                       RDEBUG2("Got additional TLS record fragment with length (%zu bytes).  "
+                               "Peer indicated more fragments to follow", frag_len);
 
-                               RDEBUG2("More Fragments with length included");
-                               return FR_TLS_MORE_FRAGMENTS_WITH_LENGTH;
+                       /*
+                        *      Check we've not exceeded the originally indicated TLS record size.
+                        */
+                       tls_session->tls_record_in_recvd_len += frag_len;
+                       if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) {
+                               RWDEBUG("Total received TLS record fragments (%zu bytes), exceeds "
+                                       "total TLS record length (%zu bytes)", frag_len, total_len);
                        }
-               } else {
-                       RDEBUG2("Length Included");
-                       return FR_TLS_LENGTH_INCLUDED;
+
+                       return FR_TLS_MORE_FRAGMENTS_WITH_LENGTH;
+               }
+
+               /*
+                *      If it's a complete record, our fragment size should match the
+                *      value of the four octet TLS length field.
+                */
+               if (total_len != frag_len) {
+                       RWDEBUG("Peer indicated no more fragments, but TLS record length (%zu bytes) "
+                               "does not match EAP-TLS data length (%zu bytes)", total_len, frag_len);
+               }
+
+               tls_session->tls_record_in_total_len = total_len;
+               tls_session->tls_record_in_recvd_len = frag_len;
+               RDEBUG2("Got complete TLS record (%zu bytes)", frag_len);
+               return FR_TLS_LENGTH_INCLUDED;
+       }
+
+       /*
+        *      The previous packet had the M flags set, but this one doesn't,
+        *      this must be the final record fragment
+        */
+       if ((eaptls_prev && TLS_MORE_FRAGMENTS(eaptls_prev->flags)) && !TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
+               RDEBUG2("Got final TLS record fragment (%zu bytes)", frag_len);
+               tls_session->tls_record_in_recvd_len += frag_len;
+               if (tls_session->tls_record_in_recvd_len != tls_session->tls_record_in_total_len) {
+                       RWDEBUG("Total received TLS record fragments (%zu bytes), does not equal indicated "
+                               "TLS record length (%zu bytes)",
+                               tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len);
                }
        }
 
        if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
-               RDEBUG2("More fragments to follow");
+               RDEBUG2("Got additional TLS record fragment (%zu bytes).  Peer indicated more fragments to follow",
+                       frag_len);
+               tls_session->tls_record_in_recvd_len += frag_len;
+               if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) {
+                       RWDEBUG("Total received TLS record fragments (%zu bytes), exceeds "
+                               "indicated TLS record length (%zu bytes)",
+                               tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len);
+               }
                return FR_TLS_MORE_FRAGMENTS;
        }
 
        /*
-        *      None of the flags are set, but it's still a valid
-        *      EAPTLS packet.
+        *      None of the flags are set, but it's still a valid EAP-TLS packet.
         */
        return FR_TLS_OK;
 }
@@ -461,8 +522,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
        uint32_t        len = 0;
        uint8_t         *data = NULL;
 
-       if (status  == FR_TLS_INVALID)
-               return NULL;
+       if (status == FR_TLS_INVALID) return NULL;
 
        /*
         *      The main EAP code & eaptls_verify() take care of
@@ -500,7 +560,8 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
         */
        if (TLS_LENGTH_INCLUDED(tlspacket->flags) &&
            (tlspacket->length < 5)) { /* flags + TLS message length */
-               RDEBUG("Invalid EAP-TLS packet received.  (Length bit is set, but no length was found.)");
+               REDEBUG("Invalid EAP-TLS packet received:  Length bit is set, "
+                       "but packet too short to contain length field");
                talloc_free(tlspacket);
                return NULL;
        }
@@ -511,28 +572,17 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
         *
         *      Likewise, if the EAP packet says N bytes, and the TLS
         *      packet says there's fewer bytes, it's a problem.
-        *
-        *      FIXME: Try to ensure that the claimed length is
-        *      consistent across multiple TLS fragments.
         */
        if (TLS_LENGTH_INCLUDED(tlspacket->flags)) {
                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");
+                       REDEBUG("Reassembled TLS record will be %u bytes, "
+                               "greater than our maximum record size (" STRINGIFY(MAX_RECORD_SIZE) " bytes)",
+                               data_len);
                        talloc_free(tlspacket);
                        return NULL;
                }
-
-#if 0
-               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");
-                       talloc_free(tlspacket);
-                       return NULL;
-               }
-#endif
        }
 
        switch (status) {
@@ -549,7 +599,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
        case FR_TLS_LENGTH_INCLUDED:
        case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH:
                if (tlspacket->length < 5) { /* flags + TLS message length */
-                       RDEBUG("Invalid EAP-TLS packet received.  (Expected length, got none.)");
+                       REDEBUG("Invalid EAP-TLS packet received: Expected length, got none");
                        talloc_free(tlspacket);
                        return NULL;
                }
@@ -582,7 +632,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
                break;
 
        default:
-               RDEBUG("Invalid EAP-TLS packet received");
+               REDEBUG("Invalid EAP-TLS packet received");
                talloc_free(tlspacket);
                return NULL;
        }
@@ -625,12 +675,10 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
  *     SSL_CTX (internally) or TLS module(explicitly). If TLS module,
  *     then how to let SSL API know about these sessions.)
  */
-static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
-                                       eap_handler_t *handler)
+static fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *handler)
 {
-       tls_session_t *tls_session;
-
-       tls_session = (tls_session_t *)handler->opaque;
+       REQUEST         *request = handler->request;
+       tls_session_t   *tls_session = handler->opaque;
 
        if ((status == FR_TLS_MORE_FRAGMENTS) ||
            (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) ||
@@ -638,7 +686,7 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
                /*
                 *      Send the ACK.
                 */
-               eaptls_send_ack(handler->eap_ds, tls_session->peap_flag);
+               eaptls_send_ack(handler, tls_session->peap_flag);
                return FR_TLS_HANDLED;
 
        }
@@ -655,7 +703,7 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
         *      is required then send another request.
         */
        if (!tls_handshake_recv(handler->request, tls_session)) {
-               DEBUG2("TLS receive handshake failed during operation");
+               REDEBUG("TLS receive handshake failed during operation");
                tls_fail(tls_session);
                return FR_TLS_FAIL;
        }
@@ -689,7 +737,7 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
        /*
         *      Who knows what happened...
         */
-       DEBUG2("TLS failed during operation");
+       REDEBUG("TLS failed during operation");
        return FR_TLS_FAIL;
 }
 
@@ -730,51 +778,57 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
 
        if (!request) return FR_TLS_FAIL;
 
-       RDEBUG2("processing EAP-TLS");
+       RDEBUG2("Continuing EAP-TLS");
+
        SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request);
 
-       if (handler->certs) pairadd(&request->packet->vps,
-                                   paircopy(request->packet, handler->certs));
+       if (handler->certs) fr_pair_add(&request->packet->vps,
+                                   fr_pair_list_copy(request->packet, handler->certs));
 
-       /* This case is when SSL generates Alert then we
-        * send that alert to the client and then send the EAP-Failure
+       /*
+        *      This case is when SSL generates Alert then we
+        *      send that alert to the client and then send the EAP-Failure
         */
        status = eaptls_verify(handler);
-       RDEBUG2("eaptls_verify returned %d\n", status);
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       }
 
        switch (status) {
        default:
        case FR_TLS_INVALID:
        case FR_TLS_FAIL:
 
-               /*
-                *      Success means that we're done the initial
-                *      handshake.  For TTLS, this means send stuff
-                *      back to the client, and the client sends us
-                *      more tunneled data.
-                */
+       /*
+        *      Success means that we're done the initial
+        *      handshake.  For TTLS, this means send stuff
+        *      back to the client, and the client sends us
+        *      more tunneled data.
+        */
        case FR_TLS_SUCCESS:
                goto done;
 
-               /*
-                *      Normal TLS request, continue with the "get rest
-                *      of fragments" phase.
-                */
+       /*
+        *      Normal TLS request, continue with the "get rest
+        *      of fragments" phase.
+        */
        case FR_TLS_REQUEST:
                eaptls_request(handler->eap_ds, tls_session);
                status = FR_TLS_HANDLED;
                goto done;
 
-               /*
-                *      The handshake is done, and we're in the "tunnel
-                *      data" phase.
-                */
+       /*
+        *      The handshake is done, and we're in the "tunnel
+        *      data" phase.
+        */
        case FR_TLS_OK:
                RDEBUG2("Done initial handshake");
 
-               /*
-                *      Get the rest of the fragments.
-                */
+       /*
+        *      Get the rest of the fragments.
+        */
        case FR_TLS_FIRST_FRAGMENT:
        case FR_TLS_MORE_FRAGMENTS:
        case FR_TLS_LENGTH_INCLUDED:
@@ -803,8 +857,8 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
        if (tlspacket->dlen !=
            (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) {
                talloc_free(tlspacket);
-               RDEBUG("Exceeded maximum record size");
-               status =FR_TLS_FAIL;
+               REDEBUG("Exceeded maximum record size");
+               status = FR_TLS_FAIL;
                goto done;
        }
 
@@ -831,8 +885,7 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
                        /*
                         *      Send the ACK.
                         */
-                       eaptls_send_ack(handler->eap_ds,
-                                       tls_session->peap_flag);
+                       eaptls_send_ack(handler, tls_session->peap_flag);
                        RDEBUG2("Init is done, but tunneled data is fragmented");
                        status = FR_TLS_HANDLED;
                        goto done;
@@ -846,6 +899,62 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
         *      Continue the handshake.
         */
        status = eaptls_operation(status, handler);
+       if (status == FR_TLS_SUCCESS) {
+#define MAX_SESSION_SIZE (256)
+               size_t size;
+               VALUE_PAIR *vps;
+               char buffer[2 * MAX_SESSION_SIZE + 1];
+               /*
+                *      Restore the cached VPs before processing the
+                *      application data.
+                */
+               size = tls_session->ssl->session->session_id_length;
+               if (size > MAX_SESSION_SIZE) size = MAX_SESSION_SIZE;
+
+               fr_bin2hex(buffer, tls_session->ssl->session->session_id, size);
+
+               vps = SSL_SESSION_get_ex_data(tls_session->ssl->session, fr_tls_ex_index_vps);
+               if (!vps) {
+                       RWDEBUG("No information in cached session %s", buffer);
+               } else {
+                       vp_cursor_t cursor;
+                       VALUE_PAIR *vp;
+
+                       RDEBUG("Adding cached attributes from session %s", buffer);
+
+                       /*
+                        *      The cbtls_get_session() function doesn't have
+                        *      access to sock->certs or handler->certs, which
+                        *      is where the certificates normally live.  So
+                        *      the certs are all in the VPS list here, and
+                        *      have to be manually extracted.
+                        */
+                       RINDENT();
+                       for (vp = fr_cursor_init(&cursor, &vps);
+                            vp;
+                            vp = fr_cursor_next(&cursor)) {
+                               /*
+                                *      TLS-* attrs get added back to
+                                *      the request list.
+                                */
+                               if ((vp->da->vendor == 0) &&
+                                   (vp->da->attr >= PW_TLS_CERT_SERIAL) &&
+                                   (vp->da->attr <= PW_TLS_CLIENT_CERT_SUBJECT_ALT_NAME_UPN)) {
+                                       /*
+                                        *      Certs already exist.  Don't re-add them.
+                                        */
+                                       if (!handler->certs) {
+                                               rdebug_pair(L_DBG_LVL_2, request, vp, "request:");
+                                               fr_pair_add(&request->packet->vps, fr_pair_copy(request->packet, vp));
+                                       }
+                               } else {
+                                       rdebug_pair(L_DBG_LVL_2, request, vp, "reply:");
+                                       fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
+                               }
+                       }
+                       REXDENT();
+               }
+       }
 
  done:
        SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL);
@@ -883,9 +992,7 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply)
         */
        eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t,
                                                  reply->length - TLS_HEADER_LEN + 1);
-       if (!eap_ds->request->type.data) {
-               return 0;
-       }
+       if (!eap_ds->request->type.data) return 0;
 
        /* EAPTLS Header length is excluded while computing EAP typelen */
        eap_ds->request->type.length = reply->length - TLS_HEADER_LEN;
@@ -901,15 +1008,18 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply)
        case FR_TLS_REQUEST:
                eap_ds->request->code = PW_EAP_REQUEST;
                break;
+
        case FR_TLS_SUCCESS:
                eap_ds->request->code = PW_EAP_SUCCESS;
                break;
+
        case FR_TLS_FAIL:
                eap_ds->request->code = PW_EAP_FAILURE;
                break;
+
        default:
                /* Should never enter here */
-               eap_ds->request->code = PW_EAP_FAILURE;
+               rad_assert(0);
                break;
        }
 
@@ -946,7 +1056,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr)
                tls_cs = cf_section_sub_find_name2(parent, TLS_CONFIG_SECTION, tls_conf_name);
 
                if (!tls_cs) {
-                       ERROR("Cannot find tls config '%s'", tls_conf_name);
+                       ERROR("Cannot find tls config \"%s\"", tls_conf_name);
                        return NULL;
                }
        } else {
@@ -958,7 +1068,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr)
                 *      We don't fall back if the 'attr' is specified, but we can't
                 *      find the section - that is just a config error.
                 */
-               INFO("debug: '%s' option missing, trying to use legacy configuration", attr);
+               INFO("TLS section \"%s\" missing, trying to use legacy configuration", attr);
                tls_cs = cf_section_sub_find(parent, "tls");
        }
 
@@ -974,7 +1084,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("Configured fragment size is too small, must be >= 100");
                return NULL;
        }
 
@@ -985,7 +1095,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("Configured fragment size is too large, must be <= 4000");
                return NULL;
        }
 
index 68fc5c2..7510cf6 100644 (file)
@@ -44,7 +44,7 @@ USES_APPLE_DEPRECATED_API     /* OpenSSL API has been deprecated by Apple */
 #include <arpa/inet.h>
 
 #ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#  include <unistd.h>
 #endif
 
 #include <freeradius-devel/radiusd.h>
@@ -57,24 +57,25 @@ 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) 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);
+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 */
-void       eaptls_gen_mppe_keys(REQUEST *request, SSL *s,
-                                char const *prf_label);
-void           eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size);
-void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header);
+void   eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *prf_label);
+void   eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size);
+void   eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header);
 
 #define BUFFER_SIZE 1024
 
-#define EAP_TLS_START          1
-#define EAP_TLS_ACK            2
-#define EAP_TLS_SUCCESS         3
-#define EAP_TLS_FAIL           4
-#define EAP_TLS_ALERT          9
+typedef enum tls_op {
+       EAP_TLS_START   = 1,
+       EAP_TLS_ACK     = 2,
+       EAP_TLS_SUCCESS = 3,
+       EAP_TLS_FAIL    = 4,
+       EAP_TLS_ALERT   = 9
+} tls_op_t;
 
 #define TLS_HEADER_LEN   4
 
@@ -96,11 +97,11 @@ typedef struct tls_packet {
 
 
 /* EAP-TLS framework */
-EAPTLS_PACKET  *eaptls_alloc(void);
-void           eaptls_free(EAPTLS_PACKET **eaptls_packet_ptr);
+EAPTLS_PACKET  *eaptls_alloc(void);
+void           eaptls_free(EAPTLS_PACKET **eaptls_packet_ptr);
 tls_session_t  *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_conf, bool client_cert);
-int            eaptls_start(EAP_DS *eap_ds, int peap);
-int            eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply);
+int            eaptls_start(EAP_DS *eap_ds, int peap);
+int            eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply);
 
 fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *key);
 
index 78e9f99..c6568ff 100644 (file)
@@ -31,7 +31,7 @@ RCSIDH(eap_methods_h, "$Id$")
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
 
-/* Field length and other arbitrary things */
+/* Code (1) + Identifier (1) + Length (2) */
 #define EAP_HEADER_LEN                 4
 
 typedef enum eap_code {
@@ -101,6 +101,8 @@ typedef enum eap_method {
        PW_EAP_MAX_TYPES                /* 54 - for validation */
 } eap_type_t;
 
+#define PW_EAP_EXPANDED_TYPE   (254)
+
 typedef enum eap_rcode {
        EAP_NOTFOUND,           //!< EAP handler data not found.
        EAP_FOUND,              //!< EAP handler data found, continue.
@@ -114,8 +116,7 @@ typedef enum eap_rcode {
 
 extern const FR_NAME_NUMBER eap_rcode_table[];
 
-/*
- * EAP-Type specific data.
+/** EAP-Type specific data
  */
 typedef struct eap_type_data {
        eap_type_t      num;
@@ -123,8 +124,7 @@ typedef struct eap_type_data {
        uint8_t         *data;
 } eap_type_data_t;
 
-/*
- * Structure to hold EAP data.
+/** Structure to hold EAP data
  *
  * length = code + id + length + type + type.data
  *     =  1   +  1 +   2    +  1   +  X
@@ -138,8 +138,7 @@ typedef struct eap_packet {
        uint8_t         *packet;
 } eap_packet_t;
 
-/*
- * Structure to represent packet format of eap *on wire*
+/** Structure to represent packet format of eap *on wire*
  */
 typedef struct eap_packet_raw {
        uint8_t         code;
index efb742a..96db30b 100644 (file)
@@ -199,11 +199,11 @@ int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply)
        }
        eap_packet = (eap_packet_raw_t *)reply->packet;
 
-       pairdelete(&(packet->vps), PW_EAP_MESSAGE, 0, TAG_ANY);
+       fr_pair_delete_by_num(&(packet->vps), PW_EAP_MESSAGE, 0, TAG_ANY);
 
        vp = eap_packet2vp(packet, eap_packet);
        if (!vp) return RLM_MODULE_INVALID;
-       pairadd(&(packet->vps), vp);
+       fr_pair_add(&(packet->vps), vp);
 
        /*
         *      EAP-Message is always associated with
@@ -212,13 +212,13 @@ int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply)
         *      Don't add a Message-Authenticator if it's already
         *      there.
         */
-       vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(packet->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
        if (!vp) {
-               vp = paircreate(packet, PW_MESSAGE_AUTHENTICATOR, 0);
+               vp = fr_pair_afrom_num(packet, PW_MESSAGE_AUTHENTICATOR, 0);
                vp->vp_length = AUTH_VECTOR_LEN;
                vp->vp_octets = talloc_zero_array(vp, uint8_t, vp->vp_length);
 
-               pairadd(&(packet->vps), vp);
+               fr_pair_add(&(packet->vps), vp);
        }
 
        /* Set request reply code, but only if it's not already set. */
@@ -270,12 +270,12 @@ VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap)
                size = total;
                if (size > 253) size = 253;
 
-               vp = paircreate(packet, PW_EAP_MESSAGE, 0);
+               vp = fr_pair_afrom_num(packet, PW_EAP_MESSAGE, 0);
                if (!vp) {
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return NULL;
                }
-               pairmemcpy(vp, ptr, size);
+               fr_pair_value_memcpy(vp, ptr, size);
 
                fr_cursor_insert(&out, vp);
 
@@ -306,7 +306,7 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
        /*
         *      Get only EAP-Message attribute list
         */
-       first = pairfind(vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+       first = fr_pair_find_by_num(vps, PW_EAP_MESSAGE, 0, TAG_ANY);
        if (!first) {
                fr_strerror_printf("EAP-Message not found");
                return NULL;
@@ -390,12 +390,12 @@ void eap_add_reply(REQUEST *request,
 {
        VALUE_PAIR *vp;
 
-       vp = pairmake_reply(name, NULL, T_OP_EQ);
+       vp = pair_make_reply(name, NULL, T_OP_EQ);
        if (!vp) {
                REDEBUG("Did not create attribute %s: %s\n",
                        name, fr_strerror());
                return;
        }
 
-       pairmemcpy(vp, value, len);
+       fr_pair_value_memcpy(vp, value, len);
 }
index 9c1c06d..f57714b 100644 (file)
@@ -36,7 +36,7 @@ RCSID("$Id$")
 
 void eapsim_calculate_keys(struct eapsim_keys *ek)
 {
-       fr_SHA1_CTX context;
+       fr_sha1_ctx context;
        uint8_t fk[160];
        unsigned char buf[256];
        unsigned char *p;
index 52955f5..19a7596 100644 (file)
@@ -56,7 +56,7 @@ RCSID("$Id$")
 #include <freeradius-devel/sha1.h>
 
 /*
- * given a radius request with many attribues in the EAP-SIM range, build
+ * given a radius request with many attributes in the EAP-SIM range, build
  * them all into a single EAP-SIM body.
  *
  */
@@ -81,13 +81,13 @@ int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
         * it might be too big for putting into an EAP-Type-SIM
         *
         */
-       subtype = (vp = pairfind(r->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) ?
+       subtype = (vp = fr_pair_find_by_num(r->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) ?
                vp->vp_integer : EAPSIM_START;
 
-       id = (vp = pairfind(r->vps, PW_EAP_ID, 0, TAG_ANY)) ?
+       id = (vp = fr_pair_find_by_num(r->vps, PW_EAP_ID, 0, TAG_ANY)) ?
                vp->vp_integer : ((int)getpid() & 0xff);
 
-       eapcode = (vp = pairfind(r->vps, PW_EAP_CODE, 0, TAG_ANY)) ?
+       eapcode = (vp = fr_pair_find_by_num(r->vps, PW_EAP_CODE, 0, TAG_ANY)) ?
                vp->vp_integer : PW_EAP_REQUEST;
 
        /*
@@ -222,7 +222,7 @@ int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
         * then we should calculate the HMAC-SHA1 of the resulting EAP-SIM
         * packet, appended with the value of append.
         */
-       vp = pairfind(r->vps, PW_EAP_SIM_KEY, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(r->vps, PW_EAP_SIM_KEY, 0, TAG_ANY);
        if(macspace != NULL && vp != NULL) {
                unsigned char           *buffer;
                eap_packet_raw_t        *hdr;
@@ -297,14 +297,14 @@ int unmap_eapsim_basictypes(RADIUS_PACKET *r,
                return 0;
        }
 
-       newvp = paircreate(r, PW_EAP_SIM_SUBTYPE, 0);
+       newvp = fr_pair_afrom_num(r, PW_EAP_SIM_SUBTYPE, 0);
        if (!newvp) {
                return 0;
        }
 
        newvp->vp_integer = attr[0];
        newvp->vp_length = 1;
-       pairadd(&(r->vps), newvp);
+       fr_pair_add(&(r->vps), newvp);
 
        attr     += 3;
        attrlen  -= 3;
@@ -337,11 +337,11 @@ int unmap_eapsim_basictypes(RADIUS_PACKET *r,
                               return 0;
                }
 
-               newvp = paircreate(r, eapsim_attribute+PW_EAP_SIM_BASE, 0);
+               newvp = fr_pair_afrom_num(r, eapsim_attribute+PW_EAP_SIM_BASE, 0);
                newvp->vp_length = eapsim_len-2;
                newvp->vp_octets = p = talloc_array(newvp, uint8_t, newvp->vp_length);
                memcpy(p, &attr[2], eapsim_len-2);
-               pairadd(&(r->vps), newvp);
+               fr_pair_add(&(r->vps), newvp);
                newvp = NULL;
 
                /* advance pointers, decrement length */
@@ -367,7 +367,7 @@ int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_S
        int elen,len;
        VALUE_PAIR *mac;
 
-       mac = pairfind(rvps, PW_EAP_SIM_MAC, 0, TAG_ANY);
+       mac = fr_pair_find_by_num(rvps, PW_EAP_SIM_MAC, 0, TAG_ANY);
 
        if(!mac || mac->vp_length != 18) {
                /* can't check a packet with no AT_MAC attribute */
index 4e17171..2002c62 100644 (file)
@@ -99,7 +99,7 @@ static void onesixty_add_mod(onesixty *sum, onesixty *a, onesixty *b)
  */
 void fips186_2prf(uint8_t mk[20], uint8_t finalkey[160])
 {
-       fr_SHA1_CTX context;
+       fr_sha1_ctx context;
        int j;
        onesixty xval, xkey, w_0, w_1, sum, one;
        uint8_t *f;
index 29f7722..7fa92bd 100644 (file)
@@ -28,6 +28,8 @@ USES_APPLE_DEPRECATED_API     /* OpenSSL API has been deprecated by Apple */
 #include "eap_tls.h"
 #include <openssl/hmac.h>
 
+
+#if OPENSSL_VERSION_NUMBER < 0x10001000L
 /*
  * TLS PRF from RFC 2246
  */
@@ -96,18 +98,17 @@ static void PRF(unsigned char const *secret, unsigned int secret_len,
                out[i] ^= buf[i];
        }
 }
+#endif
 
 #define EAPTLS_MPPE_KEY_LEN     32
 
 /*
  *     Generate keys according to RFC 2716 and add to reply
  */
-void eaptls_gen_mppe_keys(REQUEST *request, SSL *s,
-                         char const *prf_label)
+void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *prf_label)
 {
-       unsigned char out[4*EAPTLS_MPPE_KEY_LEN], buf[4*EAPTLS_MPPE_KEY_LEN];
-       unsigned char seed[64 + 2*SSL3_RANDOM_SIZE];
-       unsigned char *p = seed;
+       uint8_t out[4 * EAPTLS_MPPE_KEY_LEN];
+       uint8_t *p;
        size_t prf_size;
 
        if (!s->s3) {
@@ -117,18 +118,32 @@ void eaptls_gen_mppe_keys(REQUEST *request, SSL *s,
 
        prf_size = strlen(prf_label);
 
-       memcpy(p, prf_label, prf_size);
-       p += prf_size;
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+       if (SSL_export_keying_material(s, out, sizeof(out), prf_label, prf_size, NULL, 0, 0) != 1) {
+               ERROR("Failed generating keying material");
+               return;
+       }
+#else
+       {
+               uint8_t seed[64 + (2 * SSL3_RANDOM_SIZE)];
+               uint8_t buf[4 * EAPTLS_MPPE_KEY_LEN];
 
-       memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
-       p += SSL3_RANDOM_SIZE;
-       prf_size += SSL3_RANDOM_SIZE;
+               p = seed;
 
-       memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
-       prf_size += SSL3_RANDOM_SIZE;
+               memcpy(p, prf_label, prf_size);
+               p += prf_size;
 
-       PRF(s->session->master_key, s->session->master_key_length,
-           seed, prf_size, out, buf, sizeof(out));
+               memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
+               p += SSL3_RANDOM_SIZE;
+               prf_size += SSL3_RANDOM_SIZE;
+
+               memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
+               prf_size += SSL3_RANDOM_SIZE;
+
+               PRF(s->session->master_key, s->session->master_key_length,
+                   seed, prf_size, out, buf, sizeof(out));
+       }
+#endif
 
        p = out;
        eap_add_reply(request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
@@ -150,15 +165,21 @@ void eaptls_gen_mppe_keys(REQUEST *request, SSL *s,
  */
 void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size)
 {
+#if OPENSSL_VERSION_NUMBER < 0x10001000L
        uint8_t out[32], buf[32];
        uint8_t seed[sizeof(FR_TLS_PRF_CHALLENGE)-1 + 2*SSL3_RANDOM_SIZE];
        uint8_t *p = seed;
+#endif
 
        if (!s->s3) {
                ERROR("No SSLv3 information");
                return;
        }
 
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+       SSL_export_keying_material(s, buffer, size, FR_TLS_PRF_CHALLENGE,
+                                  sizeof(FR_TLS_PRF_CHALLENGE) - 1, NULL, 0, 0);
+#else
        memcpy(p, FR_TLS_PRF_CHALLENGE, sizeof(FR_TLS_PRF_CHALLENGE)-1);
        p += sizeof(FR_TLS_PRF_CHALLENGE)-1;
        memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
@@ -169,6 +190,7 @@ void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size)
            seed, sizeof(seed), out, buf, sizeof(out));
 
        memcpy(buffer, out, size);
+#endif
 }
 
 /*
@@ -185,7 +207,7 @@ void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header)
                return;
        }
 
-       vp = paircreate(packet, PW_EAP_SESSION_ID, 0);
+       vp = fr_pair_afrom_num(packet, PW_EAP_SESSION_ID, 0);
        if (!vp) return;
 
        vp->vp_length = 1 + 2 * SSL3_RANDOM_SIZE;
@@ -196,5 +218,5 @@ void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header)
        memcpy(p + 1 + SSL3_RANDOM_SIZE,
               s->s3->server_random, SSL3_RANDOM_SIZE);
        vp->vp_octets = p;
-       pairadd(&packet->vps, vp);
+       fr_pair_add(&packet->vps, vp);
 }
index 0ba2090..7d9d62c 100644 (file)
@@ -74,8 +74,6 @@ void eap_ds_free(EAP_DS **eap_ds_p)
 
 static int _eap_handler_free(eap_handler_t *handler)
 {
-       rlm_eap_t *inst = handler->inst_holder;
-
        if (handler->identity) {
                talloc_free(handler->identity);
                handler->identity = NULL;
@@ -92,18 +90,30 @@ static int _eap_handler_free(eap_handler_t *handler)
        handler->opaque = NULL;
        handler->free_opaque = NULL;
 
-       if (handler->certs) pairfree(&handler->certs);
+       if (handler->certs) fr_pair_list_free(&handler->certs);
 
-       PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
-       if (inst->handler_tree) {
-               rbtree_deletebydata(inst->handler_tree, handler);
-       }
        /*
-        *      Free operations need to be synchronised too.
+        *      Give helpful debug messages if:
+        *
+        *      we're debugging TLS sessions, which don't finish,
+        *      and which aren't deleted early due to a likely RADIUS
+        *      retransmit which nukes our ID, and therefore our stare.
         */
-       talloc_free(handler);
-       PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
+       if (fr_debug_lvl && handler->tls && !handler->finished &&
+           (time(NULL) > (handler->timestamp + 3))) {
+               WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+               WARN("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!                  !!",
+                    handler->state[0], handler->state[1],
+                    handler->state[2], handler->state[3],
+                    handler->state[4], handler->state[5],
+                    handler->state[6], handler->state[7]);
+
+               WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility     !!");
+               WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+       }
 
+       talloc_free(handler);
+       
        return 0;
 }
 
@@ -114,23 +124,12 @@ eap_handler_t *eap_handler_alloc(rlm_eap_t *inst)
 {
        eap_handler_t   *handler;
 
-       PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
        handler = talloc_zero(NULL, eap_handler_t);
-       if (handler == NULL) {
-               PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
+       if (!handler) {
                ERROR("Failed allocating handler");
                return NULL;
        }
-       if (inst->handler_tree) {
-               if (!rbtree_insert(inst->handler_tree, handler)) {
-                       PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
-                       ERROR("Failed inserting EAP handler into handler tree");
-                       talloc_free(handler);
-                       return NULL;
-               }
-       }
        handler->inst_holder = inst;
-       PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
 
        /* Doesn't need to be inside the critical region */
        talloc_set_destructor(handler, _eap_handler_free);
@@ -138,71 +137,6 @@ eap_handler_t *eap_handler_alloc(rlm_eap_t *inst)
        return handler;
 }
 
-typedef struct check_handler_t {
-       rlm_eap_t       *inst;
-       eap_handler_t   *handler;
-       int             trips;
-} check_handler_t;
-
-static int _check_opaque_free(check_handler_t *check)
-{
-       bool do_warning = false;
-       uint8_t state[8];
-
-       if (!check->inst || !check->handler) {
-               return 0;
-       }
-
-       if (!check->inst->handler_tree) goto done;
-
-       PTHREAD_MUTEX_LOCK(&(check->inst->handler_mutex));
-       if (!rbtree_finddata(check->inst->handler_tree, check->handler)) {
-               goto done;
-       }
-
-       /*
-        *      The session has continued *after* this packet.
-        *      Don't do a warning.
-        */
-       if (check->handler->trips > check->trips) {
-               goto done;
-       }
-
-       /*
-        *      No TLS means no warnings.
-        */
-       if (!check->handler->tls) goto done;
-
-       /*
-        *      If we're being deleted early, it's likely because we
-        *      received a transmit from the client that re-uses the
-        *      same RADIUS Id, which forces the current packet to be
-        *      deleted.  In that case, ignore the error.
-        */
-       if (time(NULL) < (check->handler->timestamp + 3)) goto done;
-
-       if (!check->handler->finished) {
-               do_warning = true;
-               memcpy(state, check->handler->state, sizeof(state));
-       }
-
-done:
-       PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex));
-
-       if (do_warning) {
-               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]);
-
-               WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility     !!");
-               WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
-       }
-
-       return 0;
-}
 
 void eaplist_free(rlm_eap_t *inst)
 {
@@ -339,7 +273,7 @@ int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
         *      Generate State, since we've been asked to add it to
         *      the list.
         */
-       state = pairmake_reply("State", NULL, T_OP_EQ);
+       state = pair_make_reply("State", NULL, T_OP_EQ);
        if (!state) return 0;
 
        /*
@@ -392,27 +326,13 @@ int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
        handler->state[5] = handler->eap_id ^ handler->state[1];
        handler->state[6] = handler->type ^ handler->state[2];
 
-       pairmemcpy(state, handler->state, sizeof(handler->state));
+       fr_pair_value_memcpy(state, handler->state, sizeof(handler->state));
 
        /*
         *      Big-time failure.
         */
        status = rbtree_insert(inst->session_tree, handler);
 
-       /*
-        *      Catch Access-Challenge without response.
-        */
-       if (inst->handler_tree) {
-               check_handler_t *check = talloc(handler, check_handler_t);
-
-               check->inst = inst;
-               check->handler = handler;
-               check->trips = handler->trips;
-
-               talloc_set_destructor(check, _check_opaque_free);
-               request_data_add(request, inst, 0, check, true);
-       }
-
        if (status) {
                eap_handler_t *prev;
 
@@ -442,7 +362,7 @@ int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
        PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
 
        if (status <= 0) {
-               pairdelete(&request->reply->vps, PW_STATE, 0, TAG_ANY);
+               fr_pair_delete_by_num(&request->reply->vps, PW_STATE, 0, TAG_ANY);
 
                if (status < 0) {
                        static time_t last_logged = 0;
@@ -485,7 +405,7 @@ eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request,
         *      We key the sessions off of the 'state' attribute, so it
         *      must exist.
         */
-       state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
+       state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
        if (!state ||
            (state->vp_length != EAP_STATE_LEN)) {
                return NULL;
index a2d8853..5cc5fa5 100644 (file)
@@ -27,6 +27,7 @@ RCSID("$Id$")
 #include <freeradius-devel/libradius.h>
 
 #include <ctype.h>
+#include <assert.h>
 
 #if HAVE_GETOPT_H
 #      include <getopt.h>
@@ -41,12 +42,15 @@ RCSID("$Id$")
 
 extern int sha1_data_problems;
 
-static unsigned int retries = 10;
-static float timeout = 3;
+#define USEC 1000000
+
+static uint32_t parallel = 1;
+static unsigned int retries = 3;
+static float timeout = 5;
+static struct timeval tv_timeout;
 static char const *secret = NULL;
 static int do_output = 1;
 static int do_summary = 0;
-static bool filedone = false;
 static int totalapp = 0;
 static int totaldeny = 0;
 static char filesecret[256];
@@ -54,224 +58,584 @@ static char const *radius_dir = NULL;
 char const *progname = "radeapclient";
 /* fr_randctx randctx; */
 
-struct main_config_t main_config;
+main_config_t main_config;
 char const *radiusd_version = "";
 
 #ifdef WITH_TLS
 #include <freeradius-devel/tls.h>
 #endif
 
-log_lvl_t debug_flag = 0;
-static char password[256];
+log_lvl_t rad_debug_lvl = 0;
+
+//TODO: move structures to a header file.
+
+typedef struct rc_input_vps_list rc_input_vps_list_t;
+typedef struct rc_input_vps rc_input_vps_t;
+typedef struct rc_transaction rc_transaction_t;
+
+/** Structure which contains EAP context, necessary to perform the full EAP transaction.
+ */
+typedef struct rc_eap_sim_context {
+       struct eapsim_keys keys;
+} rc_eap_sim_context_t;
+
+typedef struct rc_eap_md5_context {
+       int tried;
+} rc_eap_md5_context_t;
+
+typedef struct rc_eap_context {
+       int eap_type;   //!< contains the EAP-Type
+       char password[256];     //!< copy of User-Password (or CHAP-Password).
+       union {
+               rc_eap_sim_context_t sim;
+               rc_eap_md5_context_t md5;
+       } eap;
+} rc_eap_context_t;
+
+
+/** Structure which holds a list of available input vps.
+ */
+struct rc_input_vps_list {
+       rc_input_vps_t *head;
+       rc_input_vps_t *tail;
+       uint32_t size;
+};
+
+/** Structure which holds an input vps entry (read from file or stdin),
+ *  and linkage to previous / next entries.
+ */
+struct rc_input_vps {
+       uint32_t num;   //!< The number (within the file) of the input we're reading.
+
+       VALUE_PAIR *vps_in;     //!< the list of attribute/value pairs.
+
+       rc_input_vps_list_t *list;      //!< the list to which this entry belongs (NULL for an unchained entry).
+
+       rc_input_vps_t *prev;
+       rc_input_vps_t *next;
+};
+
 
-static struct eapsim_keys eapsim_mk;
+/** Structure which holds a transaction: sent packet, reply received...
+ */
+struct rc_transaction {
+       uint32_t id;    //!< id of transaction (0 for the first one).
+
+       uint32_t num_packet;    //!< number of packets sent for this transaction.
+
+       RADIUS_PACKET *packet;
+       RADIUS_PACKET *reply;
+
+       rc_input_vps_t *input_vps;
+
+       rc_eap_context_t *eap_context;
+
+       uint32_t tries;
+
+       fr_event_t *event;      //!< armed event (if any).
+
+       char            password[256];
+       char const      *name;  //!< Test name (as specified in the request).
+};
+
+
+static TALLOC_CTX *autofree;
+static uint32_t num_trans = 0; //!< number of transactions initialized.
+static uint32_t num_started = 0; //!< number of transactions started.
+static uint32_t num_ongoing = 0; //!< number of ongoing transactions.
+static uint32_t num_finished = 0; //!< number of finished transactions.
+
+static rc_input_vps_list_t rc_vps_list_in; //!< list of available input vps entries.
+static fr_packet_list_t *pl = NULL;    //!< list of outgoing packets.
+static unsigned int num_sockets = 0;   //!< number of allocated sockets.
+static fr_event_list_t *ev_list = NULL; //!< list of armed events.
+
+static int force_af = AF_UNSPEC;
+static int ipproto = IPPROTO_UDP;
+static fr_ipaddr_t server_ipaddr;
+static bool server_addr_init = false;
+static uint16_t server_port = 0;
+static int packet_code = PW_CODE_UNDEFINED;
+
+static int rc_map_eap_methods(RADIUS_PACKET *req);
+static void rc_unmap_eap_methods(RADIUS_PACKET *rep);
+static int rc_map_eapsim_types(RADIUS_PACKET *r);
+static int rc_unmap_eapsim_types(RADIUS_PACKET *r);
+
+static void rc_get_port(PW_CODE type, uint16_t *port);
+static void rc_evprep_packet_timeout(rc_transaction_t *trans);
+static void rc_deallocate_id(rc_transaction_t *trans);
 
-static void map_eap_methods(RADIUS_PACKET *req);
-static void unmap_eap_methods(RADIUS_PACKET *rep);
-static int map_eapsim_types(RADIUS_PACKET *r);
-static int unmap_eapsim_types(RADIUS_PACKET *r);
 
 static void NEVER_RETURNS usage(void)
 {
-       fprintf(stdout, "Usage: radeapclient [options] server[:port] <command> [<secret>]");
-
-       fprintf(stdout, " <command>    One of auth, acct, status, or disconnect.");
-       fprintf(stdout, " -d raddb     Set dictionary directory.");
-       fprintf(stdout, " -f file      Read packets from file, not stdin.");
-       fprintf(stdout, " -r retries   If timeout, retry sending the packet 'retries' times.");
-       fprintf(stdout, " -t timeout   Wait 'timeout' seconds before retrying (may be a floating point number).");
-       fprintf(stdout, " -h           Print usage help information.");
-       fprintf(stdout, " -i id        Set request id to 'id'.  Values may be 0..255");
-       fprintf(stdout, " -S file      read secret from file, not command line.");
-       fprintf(stdout, " -q           Do not print anything out.");
-       fprintf(stdout, " -s           Print out summary information of auth results.");
-       fprintf(stdout, " -v           Show program version information.");
-       fprintf(stdout, " -x           Debugging mode.");
-       fprintf(stdout, " -4           Use IPv4 address of server");
-       fprintf(stdout, " -6           Use IPv6 address of server.");
+       fprintf(stdout, "Usage: radeapclient [options] server[:port] <command> [<secret>]\n");
+
+       fprintf(stdout, "  <command>              One of auth, acct, status, coa, disconnect or auto.\n");
+       fprintf(stdout, "  -4                     Use IPv4 address of server\n");
+       fprintf(stdout, "  -6                     Use IPv6 address of server.\n");
+       fprintf(stdout, "  -d <raddb>             Set user dictionary directory (defaults to " RADDBDIR ").\n");
+       fprintf(stdout, "  -D <dictdir>           Set main dictionary directory (defaults to " DICTDIR ").\n");
+       fprintf(stdout, "  -f <file>              Read packets from file, not stdin.\n");
+       fprintf(stdout, "  -h                     Print usage help information.\n");
+       fprintf(stdout, "  -p <num>               Send 'num' packets in parallel.\n");
+       fprintf(stdout, "  -q                     Do not print anything out.\n");
+       fprintf(stdout, "  -r <retries>           If timeout, retry sending the packet 'retries' times.\n");
+       fprintf(stdout, "  -s                     Print out summary information of auth results.\n");
+       fprintf(stdout, "  -S <file>              read secret from file, not command line.\n");
+       fprintf(stdout, "  -t <timeout>           Wait 'timeout' seconds before retrying (may be a floating point number).\n");
+       fprintf(stdout, "  -v                     Show program version information.\n");
+       fprintf(stdout, "  -x                     Debugging mode.\n");
 
        exit(1);
 }
+
+static const FR_NAME_NUMBER rc_request_types[] = {
+       { "auth",       PW_CODE_ACCESS_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}
+};
+
 int rad_virtual_server(REQUEST UNUSED *request)
 {
   /*We're not the server so we cannot do this*/
   abort();
 }
 
-static uint16_t getport(char const *name)
+/** Convert a float to struct timeval.
+ */
+static void rc_float_to_timeval(struct timeval *tv, float f_val)
 {
-       struct  servent         *svp;
+       tv->tv_sec = (time_t)f_val;
+       uint64_t usec = (uint64_t)(f_val * USEC) - (tv->tv_sec * USEC);
+       tv->tv_usec = usec;
+}
 
-       svp = getservbyname(name, "udp");
-       if (!svp) return 0;
+/** Add an allocated rc_input_vps_t entry to the tail of the list.
+ */
+ static void rc_add_vps_entry(rc_input_vps_list_t *list, rc_input_vps_t *entry)
+{
+       if (!list || !entry) return;
 
-       return ntohs(svp->s_port);
+       if (!list->head) {
+               assert(list->tail == NULL);
+               list->head = entry;
+               entry->prev = NULL;
+       } else {
+               assert(list->tail != NULL);
+               assert(list->tail->next == NULL);
+               list->tail->next = entry;
+               entry->prev = list->tail;
+       }
+       list->tail = entry;
+       entry->next = NULL;
+       entry->list = list;
+       list->size ++;
 }
 
-#define R_RECV (0)
-#define R_SENT (1)
-static void debug_packet(RADIUS_PACKET *packet, int direction)
+/** Remove a selected rc_input_vps_t entry from its current list.
+ */
+static rc_input_vps_t *rc_yank_vps_entry(rc_input_vps_t *entry)
 {
-       VALUE_PAIR *vp;
-       char buffer[1024];
-       char const *received, *from;
-       fr_ipaddr_t const *ip;
-       uint16_t port;
+       if (!entry) return NULL;
 
-       if (!packet) return;
+       if (!entry->list) return entry; /* not in a list, nothing to do. Just return the entry. */
 
-       if (direction == 0) {
-               received = "Received";
-               from = "from";  /* what else? */
-               ip = &packet->src_ipaddr;
-               port = packet->src_port;
+       rc_input_vps_t *prev, *next;
 
-       } else {
-               received = "Sending";
-               from = "to";    /* hah! */
-               ip = &packet->dst_ipaddr;
-               port = packet->dst_port;
-       }
+       prev = entry->prev;
+       next = entry->next;
 
-       /*
-        *      Client-specific debugging re-prints the input
-        *      packet into the client log.
-        *
-        *      This really belongs in a utility library
-        */
-       if (is_radius_code(packet->code)) {
-               DEBUG("%s %s packet %s host %s port %d, id=%d, length=%d",
-                      received, fr_packet_codes[packet->code], from,
-                      inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
-                      port, packet->id, (int) packet->data_len);
-       } else {
-               DEBUG("%s packet %s host %s port %d code=%d, id=%d, length=%d",
-                      received, from,
-                      inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
-                      port,
-                      packet->code, packet->id, (int) packet->data_len);
+       rc_input_vps_list_t *list = entry->list;
+
+       assert(list->head != NULL); /* entry belongs to a list, so the list can't be empty. */
+       assert(list->tail != NULL); /* same. */
+
+       if (prev) {
+               assert(list->head != entry); /* if entry has a prev, then entry can't be head. */
+               prev->next = next;
+       }
+       else {
+               assert(list->head == entry); /* if entry has no prev, then entry must be head. */
+               list->head = next;
        }
 
-       for (vp = packet->vps; vp != NULL; vp = vp->next) {
-               vp_prints(buffer, sizeof(buffer), vp);
-               DEBUG("\t%s", buffer);
+       if (next) {
+               assert(list->tail != entry); /* if entry has a next, then entry can't be tail. */
+               next->prev = prev;
+       }
+       else {
+               assert(list->tail == entry); /* if entry has no next, then entry must be tail. */
+               list->tail = prev;
        }
-       fflush(stdout);
-}
 
+       entry->list = NULL;
+       entry->prev = NULL;
+       entry->next = NULL;
+       list->size --;
+       return entry;
+}
 
-static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
+/** Load input entries (list of vps) from a file or stdin, and add them to the list.
+ *  They will be used to initiate transactions.
+ */
+static int rc_load_input(TALLOC_CTX *ctx, char const *filename, rc_input_vps_list_t *list, uint32_t max_entries)
 {
-       unsigned int i;
-       struct timeval  tv;
+       FILE *file_in = NULL;
+       bool file_done = false;
+       rc_input_vps_t *request;
+       char const *input;
+       uint32_t input_num = 0;
+
+       /* Determine where to read the VP's from. */
+       if (filename && strcmp(filename, "-") != 0) {
+               DEBUG2("Opening input file: %s", filename);
+               file_in = fopen(filename, "r");
+               if (!file_in) {
+                       ERROR("Error opening %s: %s", filename, strerror(errno));
+                       return 0;
+               }
+               input = filename;
+       } else {
+               DEBUG2("Reading input vps from stdin");
+               file_in = stdin;
+               input = "stdin";
+       }
 
-       if (!req || !rep) return -1;
+       /* Loop over the file (or stdin). */
+       do {
+               input_num ++;
+               MEM(request = talloc_zero(ctx, rc_input_vps_t));
+
+               if (fr_pair_list_afrom_file(request, &request->vps_in, file_in, &file_done) < 0) {
+                       ERROR("Error parsing entry %u from input: %s", input_num, input);
+                       talloc_free(request);
+                       break;
+               }
+               if (NULL == request->vps_in) {
+                       /* Last line might be empty, in this case fr_pair_list_afrom_file will return a NULL vps pointer. Silently ignore this. */
+                       talloc_free(request);
+                       break;
+               }
 
-       for (i = 0; i <= retries; i++) {
-               fd_set          rdfdesc;
+               /* Add that to the list */
+               rc_add_vps_entry(list, request);
 
-               debug_packet(req, R_SENT);
+               request->num = list->size;
 
-               if (rad_send(req, NULL, secret) < 0) {
-                       ERROR("%s", fr_strerror());
-                       exit(1);
+               if (max_entries && list->size >= max_entries) {
+                       /* Only load what we need. */
+                       break;
                }
+       } while (!file_done);
+
+       if (file_in != stdin) fclose(file_in);
+
+       /* And we're done. */
+       DEBUG("Read %d element(s) from input: %s", list->size, input);
+       return 1;
+}
 
-               /* And wait for reply, timing out as necessary */
-               FD_ZERO(&rdfdesc);
-               FD_SET(req->sockfd, &rdfdesc);
+/** Perform packet initialization for a transaction.
+ */
+static int rc_init_packet(rc_transaction_t *trans)
+{
+       if (!trans || !trans->packet) return 0;
 
-               tv.tv_sec = (int)timeout;
-               tv.tv_usec = 1000000 * (timeout - (int) timeout);
+       RADIUS_PACKET *packet = trans->packet;
+       vp_cursor_t cursor;
+       VALUE_PAIR *vp;
 
-               /* Something's wrong if we don't get exactly one fd. */
-               if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) {
-                       continue;
+       /*
+        *      Process special attributes
+        */
+       for (vp = fr_cursor_init(&cursor, &packet->vps);
+                vp;
+                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->type = VT_DATA;
+                       vp->vp_strvalue = vp->value.xlat;
+                       vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
                }
 
-               *rep = rad_recv(NULL, req->sockfd, 0);
-               if (*rep != NULL) {
-                       /*
-                        *      If we get a response from a machine
-                        *      which we did NOT send a request to,
-                        *      then complain.
-                        */
-                       if (((*rep)->src_ipaddr.af != req->dst_ipaddr.af) ||
-                           (memcmp(&(*rep)->src_ipaddr.ipaddr,
-                                   &req->dst_ipaddr.ipaddr,
-                                   ((req->dst_ipaddr.af == AF_INET ? /* AF_INET6? */
-                                     sizeof(req->dst_ipaddr.ipaddr.ip4addr) : /* FIXME: AF_INET6 */
-                                     sizeof(req->dst_ipaddr.ipaddr.ip6addr)))) != 0) ||
-                           ((*rep)->src_port != req->dst_port)) {
-                               char src[128], dst[128];
-
-                               ip_ntoh(&(*rep)->src_ipaddr, src, sizeof(src));
-                               ip_ntoh(&req->dst_ipaddr, dst, sizeof(dst));
-                               ERROR("ERROR: Sent request to host %s port %d, got response from host %s port %d!",
-                                       dst, req->dst_port,
-                                       src, (*rep)->src_port);
+               if (!vp->da->vendor) switch (vp->da->attr) {
+               default:
+                       break;
+
+               /*
+                *      Allow it to set the packet type in
+                *      the attributes read from the file.
+                */
+               case PW_PACKET_TYPE:
+                       packet->code = vp->vp_integer;
+                       break;
+
+               case PW_PACKET_DST_PORT:
+                       packet->dst_port = (vp->vp_integer & 0xffff);
+                       break;
+
+               case PW_PACKET_DST_IP_ADDRESS:
+                       packet->dst_ipaddr.af = AF_INET;
+                       packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+                       packet->dst_ipaddr.prefix = 32;
+                       break;
+
+               case PW_PACKET_DST_IPV6_ADDRESS:
+                       packet->dst_ipaddr.af = AF_INET6;
+                       packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
+                       packet->dst_ipaddr.prefix = 128;
+                       break;
+
+               case PW_PACKET_SRC_PORT:
+                       if ((vp->vp_integer < 1024) ||
+                               (vp->vp_integer > 65535)) {
+                               DEBUG("Invalid value '%u' for Packet-Src-Port", vp->vp_integer);
+                       } else {
+                               packet->src_port = (vp->vp_integer & 0xffff);
+                       }
+                       break;
+
+               case PW_PACKET_SRC_IP_ADDRESS:
+                       packet->src_ipaddr.af = AF_INET;
+                       packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+                       packet->src_ipaddr.prefix = 32;
+                       break;
+
+               case PW_PACKET_SRC_IPV6_ADDRESS:
+                       packet->src_ipaddr.af = AF_INET6;
+                       packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
+                       packet->src_ipaddr.prefix = 128;
+                       break;
+
+               case PW_DIGEST_REALM:
+               case PW_DIGEST_NONCE:
+               case PW_DIGEST_METHOD:
+               case PW_DIGEST_URI:
+               case PW_DIGEST_QOP:
+               case PW_DIGEST_ALGORITHM:
+               case PW_DIGEST_BODY_DIGEST:
+               case PW_DIGEST_CNONCE:
+               case PW_DIGEST_NONCE_COUNT:
+               case PW_DIGEST_USER_NAME:
+               /* overlapping! */
+               {
+                       DICT_ATTR const *da;
+                       uint8_t *p, *q;
+
+                       p = talloc_array(vp, uint8_t, vp->vp_length + 2);
+
+                       memcpy(p + 2, vp->vp_octets, vp->vp_length);
+                       p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
+                       vp->vp_length += 2;
+                       p[1] = vp->vp_length;
+
+                       da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
+                       if (!da) {
+                               ERROR("Attribute 'Digest-Attributes' not found by value");
                                exit(1);
                        }
+                       vp->da = da;
+
+                       /*
+                        *      Re-do fr_pair_value_memsteal ourselves,
+                        *      because we play games with
+                        *      vp->da, and fr_pair_value_memsteal 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;
-               } else {        /* NULL: couldn't receive the packet */
-                       ERROR("%s", fr_strerror());
-                       exit(1);
+
+               /*
+                *      Keep a copy of the the password attribute.
+                */
+               case PW_USER_PASSWORD:
+               case PW_CHAP_PASSWORD:
+               case PW_MS_CHAP_PASSWORD:
+                       strlcpy(trans->password, vp->vp_strvalue, sizeof(trans->password));
+                       break;
+
+               case PW_RADCLIENT_TEST_NAME:
+                       trans->name = vp->vp_strvalue;
+                       break;
+               }
+       } /* loop over the VP's we read in */
+
+       if (packet->dst_port == 0) packet->dst_port = server_port;
+
+       if (packet->dst_ipaddr.af == AF_UNSPEC) {
+               if (!server_addr_init) {
+                       DEBUG("No server was given, and input entry %u did not contain Packet-Dst-IP-Address, ignored.", trans->input_vps->num);
+                       return 0;
                }
+               packet->dst_ipaddr = server_ipaddr;
        }
 
-       /* No response or no data read (?) */
-       if (i == retries) {
-               ERROR("rad_client: no response from server");
-               exit(1);
+       /* Use the default set on the command line. */
+       if (packet->code == PW_CODE_UNDEFINED) {
+               if (packet_code == PW_CODE_UNDEFINED) {
+                       DEBUG("No packet type was given, and input entry %u did not contain Packet-Type, ignored.", trans->input_vps->num);
+                       return 0;
+               }
+               packet->code = packet_code;
        }
 
-       /*
-        *      FIXME: Discard the packet & listen for another.
-        *
-        *      Hmm... we should really be using eapol_test, which does
-        *      a lot more than radeapclient.
-        */
-       if (rad_verify(*rep, req, secret) != 0) {
-               ERROR("rad_verify: %s", fr_strerror());
-               exit(1);
+       /* Automatically set the dst port (if one wasn't already set). */
+       if (packet->dst_port == 0) {
+               rc_get_port(packet->code, &packet->dst_port);
+               if (packet->dst_port == 0) {
+                       DEBUG("Can't determine destination port for input entry %u, ignored.", trans->input_vps->num);
+                       return 0;
+               }
        }
 
-       if (rad_decode(*rep, req, secret) != 0) {
-               ERROR("rad_decode: %s", fr_strerror());
-               exit(1);
+       packet->sockfd = -1;
+
+       /* Done. */
+       return 1;
+}
+
+/** Map EAP methods and build EAP-Message (if EAP is involved).
+ *  Also allocate the EAP context.
+ */
+static void rc_build_eap_context(rc_transaction_t *trans)
+{
+       if (!trans || !trans->packet) return;
+
+       RADIUS_PACKET *packet = trans->packet;
+
+       /* Build EAP-Message (if EAP is involved. Otherwise, do nothing). */
+       int eap_type = rc_map_eap_methods(packet);
+
+       if (eap_type) {
+               if (!trans->eap_context) {
+                       MEM(trans->eap_context = talloc_zero(trans, rc_eap_context_t));
+               }
+               trans->eap_context->eap_type = eap_type;
+
+               /*
+                *      Keep a copy of the the User-Password or CHAP-Password.
+                *      Note: this is not useful for EAP-SIM, but we cannot know what kind
+                *      of challenge the server will issue.
+                */
+               VALUE_PAIR *vp;
+               vp = fr_pair_find_by_num(packet->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+               if (!vp) vp = fr_pair_find_by_num(packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+               if (!vp) vp = fr_pair_find_by_num(packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
+               if (vp) {
+                       strlcpy(trans->eap_context->password, vp->vp_strvalue, sizeof(trans->eap_context->password));
+               }
        }
+}
 
-       /* libradius debug already prints out the value pairs for us */
-       if (!fr_debug_flag && do_output) {
-               debug_packet(*rep, R_RECV);
+/** Grab an element from the input list. Initialize a new transaction context, using this element.
+ */
+static rc_transaction_t *rc_init_transaction(TALLOC_CTX *ctx)
+{
+       if (!rc_vps_list_in.head || rc_vps_list_in.size == 0) {
+               /* Empty list, can't create a new transaction. */
+               return NULL;
        }
-       if ((*rep)->code == PW_CODE_ACCESS_ACCEPT) {
-               totalapp++;
-       } else if ((*rep)->code == PW_CODE_ACCESS_REJECT) {
-               totaldeny++;
+
+       rc_input_vps_t *vps_entry = rc_vps_list_in.head;
+
+       rc_yank_vps_entry(vps_entry); /* This cannot fail (we checked the list beforehand.) */
+
+       /* We grabbed an vps entry, now we can initialize a new transaction. */
+       rc_transaction_t *trans;
+       MEM(trans = talloc_zero(ctx, rc_transaction_t));
+
+       trans->input_vps = vps_entry;
+       trans->id = num_trans ++;
+
+       talloc_steal(trans, vps_entry); /* It's ours now. */
+
+       RADIUS_PACKET *packet;
+       MEM(packet = rad_alloc(trans, 1));
+       trans->packet = packet;
+
+       /* Fill in the packet value pairs. */
+       packet->vps = fr_pair_list_copy(packet, vps_entry->vps_in);
+
+       /* Initialize the transaction packet. */
+       if (!rc_init_packet(trans)) {
+               /* Failed... */
+               talloc_free(trans);
+               return NULL;
        }
 
-       return 0;
+       /* Update transactions counters. */
+       num_started ++;
+       num_ongoing ++;
+
+       return trans;
 }
 
-static void cleanresp(RADIUS_PACKET *resp)
+/** Terminate a transaction.
+ */
+static void rc_finish_transaction(rc_transaction_t *trans)
 {
-       VALUE_PAIR *vpnext, *vp, **last;
+       if (!trans) return;
+
+       if (trans->event) fr_event_delete(ev_list, &trans->event);
+       rc_deallocate_id(trans);
+       talloc_free(trans);
+
+       /* Update transactions counters. */
+       num_ongoing --;
+       num_finished ++;
+
+       DEBUG4("pl: %d, ev: %d, in: %d", fr_packet_list_num_outgoing(pl), fr_event_list_num_elements(ev_list), rc_vps_list_in.size);
+}
+
+
+static uint16_t getport(char const *name)
+{
+       struct  servent         *svp;
+
+       svp = getservbyname(name, "udp");
+       if (!svp) return 0;
+
+       return ntohs(svp->s_port);
+}
+
 
+static void rc_cleanresp(RADIUS_PACKET *resp)
+{
+       VALUE_PAIR *vpnext, *vp, **last;
 
        /*
         * maybe should just copy things we care about, or keep
         * a copy of the original input and start from there again?
         */
-       pairdelete(&resp->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
-       pairdelete(&resp->vps, PW_EAP_TYPE_BASE+PW_EAP_IDENTITY, 0, TAG_ANY);
+       fr_pair_delete_by_num(&resp->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+       fr_pair_delete_by_num(&resp->vps, PW_EAP_TYPE_BASE+PW_EAP_IDENTITY, 0, TAG_ANY);
 
        last = &resp->vps;
-       for(vp = *last; vp != NULL; vp = vpnext)
+       for (vp = *last; vp != NULL; vp = vpnext)
        {
                vpnext = vp->next;
 
-               if((vp->da->attr > PW_EAP_TYPE_BASE &&
+               if ((vp->da->attr > PW_EAP_TYPE_BASE &&
                    vp->da->attr <= PW_EAP_TYPE_BASE+256) ||
                   (vp->da->attr > PW_EAP_SIM_BASE &&
                    vp->da->attr <= PW_EAP_SIM_BASE+256))
@@ -289,8 +653,8 @@ static void cleanresp(RADIUS_PACKET *resp)
  *
  * pick a supported version, put it into the reply, and insert a nonce.
  */
-static int process_eap_start(RADIUS_PACKET *req,
-                            RADIUS_PACKET *rep)
+static int rc_process_eap_start(rc_eap_context_t *eap_context,
+                                RADIUS_PACKET *req, RADIUS_PACKET *rep)
 {
        VALUE_PAIR *vp, *newvp;
        VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
@@ -299,9 +663,9 @@ static int process_eap_start(RADIUS_PACKET *req,
        unsigned int i,versioncount;
 
        /* form new response clear of any EAP stuff */
-       cleanresp(rep);
+       rc_cleanresp(rep);
 
-       if((vp = pairfind(req->vps, PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
+       if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
                ERROR("illegal start message has no VERSION_LIST");
                return 0;
        }
@@ -309,7 +673,7 @@ static int process_eap_start(RADIUS_PACKET *req,
        versions = (uint16_t const *) vp->vp_strvalue;
 
        /* verify that the attribute length is big enough for a length field */
-       if(vp->vp_length < 4)
+       if (vp->vp_length < 4)
        {
                ERROR("start message has illegal VERSION_LIST. Too short: %u", (unsigned int) vp->vp_length);
                return 0;
@@ -319,7 +683,7 @@ static int process_eap_start(RADIUS_PACKET *req,
        /* verify that the attribute length is big enough for the given number
         * of versions present.
         */
-       if((unsigned)vp->vp_length <= (versioncount*2 + 2))
+       if ((unsigned)vp->vp_length <= (versioncount*2 + 2))
        {
                ERROR("start message is too short. Claimed %d versions does not fit in %u bytes", versioncount, (unsigned int) vp->vp_length);
                return 0;
@@ -328,26 +692,26 @@ static int process_eap_start(RADIUS_PACKET *req,
        /*
         * record the versionlist for the MK calculation.
         */
-       eapsim_mk.versionlistlen = versioncount*2;
-       memcpy(eapsim_mk.versionlist, (unsigned char const *)(versions+1),
-              eapsim_mk.versionlistlen);
+       eap_context->eap.sim.keys.versionlistlen = versioncount*2;
+       memcpy(eap_context->eap.sim.keys.versionlist, (unsigned char const *)(versions+1),
+              eap_context->eap.sim.keys.versionlistlen);
 
        /* walk the version list, and pick the one we support, which
         * at present, is 1, EAP_SIM_VERSION.
         */
        selectedversion=0;
-       for(i=0; i < versioncount; i++)
+       for (i=0; i < versioncount; i++)
        {
-               if(ntohs(versions[i+1]) == EAP_SIM_VERSION)
+               if (ntohs(versions[i+1]) == EAP_SIM_VERSION)
                {
                        selectedversion=EAP_SIM_VERSION;
                        break;
                }
        }
-       if(selectedversion == 0)
+       if (selectedversion == 0)
        {
                ERROR("eap-sim start message. No compatible version found. We need %d", EAP_SIM_VERSION);
-               for(i=0; i < versioncount; i++)
+               for (i=0; i < versioncount; i++)
                {
                        ERROR("\tfound version %d",
                                ntohs(versions[i+1]));
@@ -360,11 +724,11 @@ static int process_eap_start(RADIUS_PACKET *req,
         * anyway we like, but it is illegal to have more than one
         * present.
         */
-       anyidreq_vp = pairfind(req->vps, PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
-       fullauthidreq_vp = pairfind(req->vps, PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
-       permanentidreq_vp = pairfind(req->vps, PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);
+       anyidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
+       fullauthidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
+       permanentidreq_vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);
 
-       if(!fullauthidreq_vp ||
+       if (!fullauthidreq_vp ||
           anyidreq_vp != NULL ||
           permanentidreq_vp != NULL) {
                ERROR("start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.",
@@ -377,9 +741,9 @@ static int process_eap_start(RADIUS_PACKET *req,
        /* okay, we have just any_id_req there, so fill in response */
 
        /* mark the subtype as being EAP-SIM/Response/Start */
-       newvp = paircreate(rep, PW_EAP_SIM_SUBTYPE, 0);
+       newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SUBTYPE, 0);
        newvp->vp_integer = EAPSIM_START;
-       pairreplace(&(rep->vps), newvp);
+       fr_pair_replace(&(rep->vps), newvp);
 
        /* insert selected version into response. */
        {
@@ -387,12 +751,12 @@ static int process_eap_start(RADIUS_PACKET *req,
 
                no_versions = htons(selectedversion);
 
-               newvp = paircreate(rep, PW_EAP_SIM_SELECTED_VERSION, 0);
-               pairmemcpy(newvp, (uint8_t *) &no_versions, 2);
-               pairreplace(&(rep->vps), newvp);
+               newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SELECTED_VERSION, 0);
+               fr_pair_value_memcpy(newvp, (uint8_t *) &no_versions, 2);
+               fr_pair_replace(&(rep->vps), newvp);
 
                /* record the selected version */
-               memcpy(eapsim_mk.versionselect, &no_versions, 2);
+               memcpy(eap_context->eap.sim.keys.versionselect, &no_versions, 2);
        }
 
        vp = newvp = NULL;
@@ -408,16 +772,16 @@ static int process_eap_start(RADIUS_PACKET *req,
                nonce[2]=fr_rand();
                nonce[3]=fr_rand();
 
-               newvp = paircreate(rep, PW_EAP_SIM_NONCE_MT, 0);
+               newvp = fr_pair_afrom_num(rep, 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);
+               fr_pair_value_memsteal(newvp, p);
 
-               pairreplace(&(rep->vps), newvp);
+               fr_pair_replace(&(rep->vps), newvp);
 
                /* also keep a copy of the nonce! */
-               memcpy(eapsim_mk.nonce_mt, nonce, 16);
+               memcpy(eap_context->eap.sim.keys.nonce_mt, nonce, 16);
        }
 
        {
@@ -428,26 +792,26 @@ static int process_eap_start(RADIUS_PACKET *req,
                /*
                 * insert the identity here.
                 */
-               vp = pairfind(rep->vps, PW_USER_NAME, 0, TAG_ANY);
-               if(!vp)
+               vp = fr_pair_find_by_num(rep->vps, PW_USER_NAME, 0, TAG_ANY);
+               if (!vp)
                {
                        ERROR("eap-sim: We need to have a User-Name attribute!");
                        return 0;
                }
-               newvp = paircreate(rep, PW_EAP_SIM_IDENTITY, 0);
+               newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_IDENTITY, 0);
 
                idlen = strlen(vp->vp_strvalue);
                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);
+               fr_pair_value_memsteal(newvp, p);
 
-               pairreplace(&(rep->vps), newvp);
+               fr_pair_replace(&(rep->vps), newvp);
 
                /* record it */
-               memcpy(eapsim_mk.identity, vp->vp_strvalue, idlen);
-               eapsim_mk.identitylen = idlen;
+               memcpy(eap_context->eap.sim.keys.identity, vp->vp_strvalue, idlen);
+               eap_context->eap.sim.keys.identitylen = idlen;
        }
 
        return 1;
@@ -463,7 +827,8 @@ static int process_eap_start(RADIUS_PACKET *req,
  * values.
  *
  */
-static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
+static int rc_process_eap_challenge(rc_eap_context_t *eap_context,
+                                    RADIUS_PACKET *req, RADIUS_PACKET *rep)
 {
        VALUE_PAIR *newvp;
        VALUE_PAIR *mac, *randvp;
@@ -472,9 +837,9 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
        uint8_t calcmac[20];
 
        /* look for the AT_MAC and the challenge data */
-       mac   = pairfind(req->vps, PW_EAP_SIM_MAC, 0, TAG_ANY);
-       randvp= pairfind(req->vps, PW_EAP_SIM_RAND, 0, TAG_ANY);
-       if(!mac || !randvp) {
+       mac   = fr_pair_find_by_num(req->vps, PW_EAP_SIM_MAC, 0, TAG_ANY);
+       randvp= fr_pair_find_by_num(req->vps, PW_EAP_SIM_RAND, 0, TAG_ANY);
+       if (!mac || !randvp) {
                ERROR("challenge message needs to contain RAND and MAC");
                return 0;
        }
@@ -491,28 +856,28 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
          randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
          randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2];
 
-         randcfgvp[0] = pairfind(rep->vps, PW_EAP_SIM_RAND1, 0, TAG_ANY);
-         randcfgvp[1] = pairfind(rep->vps, PW_EAP_SIM_RAND2, 0, TAG_ANY);
-         randcfgvp[2] = pairfind(rep->vps, PW_EAP_SIM_RAND3, 0, TAG_ANY);
+         randcfgvp[0] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND1, 0, TAG_ANY);
+         randcfgvp[1] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND2, 0, TAG_ANY);
+         randcfgvp[2] = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_RAND3, 0, TAG_ANY);
 
-         if(!randcfgvp[0] ||
+         if (!randcfgvp[0] ||
             !randcfgvp[1] ||
             !randcfgvp[2]) {
            ERROR("needs to have rand1, 2 and 3 set.");
            return 0;
          }
 
-         if(memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
+         if (memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
             memcmp(randcfg[1], randcfgvp[1]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
             memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) {
            int rnum,i,j;
 
            ERROR("one of rand 1,2,3 didn't match");
-           for(rnum = 0; rnum < 3; rnum++) {
+           for (rnum = 0; rnum < 3; rnum++) {
              ERROR("received   rand %d: ", rnum);
              j=0;
              for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
-               if(j==4) {
+               if (j==4) {
                  DEBUG("_");
                  j=0;
                }
@@ -523,7 +888,7 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
              ERROR("configured rand %d: ", rnum);
              j=0;
              for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
-               if(j==4) {
+               if (j==4) {
                  DEBUG("_");
                  j=0;
                }
@@ -543,44 +908,44 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
         * Really, they should be calculated from the RAND!
         *
         */
-       sres1 = pairfind(rep->vps, PW_EAP_SIM_SRES1, 0, TAG_ANY);
-       sres2 = pairfind(rep->vps, PW_EAP_SIM_SRES2, 0, TAG_ANY);
-       sres3 = pairfind(rep->vps, PW_EAP_SIM_SRES3, 0, TAG_ANY);
+       sres1 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES1, 0, TAG_ANY);
+       sres2 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES2, 0, TAG_ANY);
+       sres3 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_SRES3, 0, TAG_ANY);
 
-       if(!sres1 ||
+       if (!sres1 ||
           !sres2 ||
           !sres3) {
                ERROR("needs to have sres1, 2 and 3 set.");
                return 0;
        }
-       memcpy(eapsim_mk.sres[0], sres1->vp_strvalue, sizeof(eapsim_mk.sres[0]));
-       memcpy(eapsim_mk.sres[1], sres2->vp_strvalue, sizeof(eapsim_mk.sres[1]));
-       memcpy(eapsim_mk.sres[2], sres3->vp_strvalue, sizeof(eapsim_mk.sres[2]));
+       memcpy(eap_context->eap.sim.keys.sres[0], sres1->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[0]));
+       memcpy(eap_context->eap.sim.keys.sres[1], sres2->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[1]));
+       memcpy(eap_context->eap.sim.keys.sres[2], sres3->vp_strvalue, sizeof(eap_context->eap.sim.keys.sres[2]));
 
-       Kc1 = pairfind(rep->vps, PW_EAP_SIM_KC1, 0, TAG_ANY);
-       Kc2 = pairfind(rep->vps, PW_EAP_SIM_KC2, 0, TAG_ANY);
-       Kc3 = pairfind(rep->vps, PW_EAP_SIM_KC3, 0, TAG_ANY);
+       Kc1 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC1, 0, TAG_ANY);
+       Kc2 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC2, 0, TAG_ANY);
+       Kc3 = fr_pair_find_by_num(rep->vps, PW_EAP_SIM_KC3, 0, TAG_ANY);
 
-       if(!Kc1 ||
+       if (!Kc1 ||
           !Kc2 ||
           !Kc3) {
                ERROR("needs to have Kc1, 2 and 3 set.");
                return 0;
        }
-       memcpy(eapsim_mk.Kc[0], Kc1->vp_strvalue, sizeof(eapsim_mk.Kc[0]));
-       memcpy(eapsim_mk.Kc[1], Kc2->vp_strvalue, sizeof(eapsim_mk.Kc[1]));
-       memcpy(eapsim_mk.Kc[2], Kc3->vp_strvalue, sizeof(eapsim_mk.Kc[2]));
+       memcpy(eap_context->eap.sim.keys.Kc[0], Kc1->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[0]));
+       memcpy(eap_context->eap.sim.keys.Kc[1], Kc2->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[1]));
+       memcpy(eap_context->eap.sim.keys.Kc[2], Kc3->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[2]));
 
        /* all set, calculate keys */
-       eapsim_calculate_keys(&eapsim_mk);
+       eapsim_calculate_keys(&eap_context->eap.sim.keys);
 
-       if(debug_flag) {
-         eapsim_dump_mk(&eapsim_mk);
+       if (rad_debug_lvl) {
+         eapsim_dump_mk(&eap_context->eap.sim.keys);
        }
 
        /* verify the MAC, now that we have all the keys. */
-       if(eapsim_checkmac(NULL, req->vps, eapsim_mk.K_aut,
-                          eapsim_mk.nonce_mt, sizeof(eapsim_mk.nonce_mt),
+       if (eapsim_checkmac(NULL, req->vps, eap_context->eap.sim.keys.K_aut,
+                          eap_context->eap.sim.keys.nonce_mt, sizeof(eap_context->eap.sim.keys.nonce_mt),
                           calcmac)) {
                DEBUG("MAC check succeed");
        } else {
@@ -588,7 +953,7 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
                j=0;
                DEBUG("calculated MAC (");
                for (i = 0; i < 20; i++) {
-                       if(j==4) {
+                       if (j==4) {
                                printf("_");
                                j=0;
                        }
@@ -601,12 +966,12 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
        }
 
        /* form new response clear of any EAP stuff */
-       cleanresp(rep);
+       rc_cleanresp(rep);
 
        /* mark the subtype as being EAP-SIM/Response/Start */
-       newvp = paircreate(rep, PW_EAP_SIM_SUBTYPE, 0);
+       newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_SUBTYPE, 0);
        newvp->vp_integer = EAPSIM_CHALLENGE;
-       pairreplace(&(rep->vps), newvp);
+       fr_pair_replace(&(rep->vps), newvp);
 
        {
                uint8_t *p;
@@ -614,21 +979,21 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
                 * fill the SIM_MAC with a field that will in fact get appended
                 * to the packet before the MAC is calculated
                 */
-               newvp = paircreate(rep, PW_EAP_SIM_MAC, 0);
+               newvp = fr_pair_afrom_num(rep, 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);
+               fr_pair_value_memsteal(newvp, p);
 
-               pairreplace(&(rep->vps), newvp);
+               fr_pair_replace(&(rep->vps), newvp);
        }
 
-       newvp = paircreate(rep, PW_EAP_SIM_KEY, 0);
-       pairmemcpy(newvp, eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);
+       newvp = fr_pair_afrom_num(rep, PW_EAP_SIM_KEY, 0);
+       fr_pair_value_memcpy(newvp, eap_context->eap.sim.keys.K_aut, EAPSIM_AUTH_SIZE);
 
-       pairreplace(&(rep->vps), newvp);
+       fr_pair_replace(&(rep->vps), newvp);
 
        return 1;
 }
@@ -639,42 +1004,43 @@ static int process_eap_challenge(RADIUS_PACKET *req, RADIUS_PACKET *rep)
  * the *reponse* is to the server.
  *
  */
-static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp)
+static int rc_respond_eap_sim(rc_eap_context_t *eap_context,
+                           RADIUS_PACKET *req, RADIUS_PACKET *resp)
 {
        enum eapsim_clientstates state, newstate;
        enum eapsim_subtype subtype;
        VALUE_PAIR *vp, *statevp, *radstate, *eapid;
        char statenamebuf[32], subtypenamebuf[32];
 
-       if ((radstate = paircopy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
+       if ((radstate = fr_pair_list_copy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
        {
                return 0;
        }
 
-       if ((eapid = paircopy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
+       if ((eapid = fr_pair_list_copy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
        {
                return 0;
        }
 
        /* first, dig up the state from the request packet, setting
-        * outselves to be in EAP-SIM-Start state if there is none.
+        * ourselves to be in EAP-SIM-Start state if there is none.
         */
 
-       if((statevp = pairfind(resp->vps, PW_EAP_SIM_STATE, 0, TAG_ANY)) == NULL)
+       if ((statevp = fr_pair_find_by_num(resp->vps, PW_EAP_SIM_STATE, 0, TAG_ANY)) == NULL)
        {
                /* must be initial request */
-               statevp = paircreate(resp, PW_EAP_SIM_STATE, 0);
+               statevp = fr_pair_afrom_num(resp, PW_EAP_SIM_STATE, 0);
                statevp->vp_integer = EAPSIM_CLIENT_INIT;
-               pairreplace(&(resp->vps), statevp);
+               fr_pair_replace(&(resp->vps), statevp);
        }
        state = statevp->vp_integer;
 
        /*
         * map the attributes, and authenticate them.
         */
-       unmap_eapsim_types(req);
+       rc_unmap_eapsim_types(req);
 
-       if((vp = pairfind(req->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL)
+       if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL)
        {
                return 0;
        }
@@ -687,7 +1053,7 @@ static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp)
        case EAPSIM_CLIENT_INIT:
                switch (subtype) {
                case EAPSIM_START:
-                       newstate = process_eap_start(req, resp);
+                       newstate = rc_process_eap_start(eap_context, req, resp);
                        break;
 
                case EAPSIM_CHALLENGE:
@@ -706,11 +1072,11 @@ static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp)
                switch (subtype) {
                case EAPSIM_START:
                        /* NOT SURE ABOUT THIS ONE, retransmit, I guess */
-                       newstate = process_eap_start(req, resp);
+                       newstate = rc_process_eap_start(eap_context, req, resp);
                        break;
 
                case EAPSIM_CHALLENGE:
-                       newstate = process_eap_challenge(req, resp);
+                       newstate = rc_process_eap_challenge(eap_context, req, resp);
                        break;
 
                default:
@@ -730,20 +1096,20 @@ static int respond_eap_sim(RADIUS_PACKET *req, RADIUS_PACKET *resp)
        }
 
        /* copy the eap state object in */
-       pairreplace(&(resp->vps), eapid);
+       fr_pair_replace(&(resp->vps), eapid);
 
        /* update stete info, and send new packet */
-       map_eapsim_types(resp);
+       rc_map_eapsim_types(resp);
 
        /* copy the radius state object in */
-       pairreplace(&(resp->vps), radstate);
+       fr_pair_replace(&(resp->vps), radstate);
 
        statevp->vp_integer = newstate;
        return 1;
 }
 
-static int respond_eap_md5(RADIUS_PACKET *req,
-                          RADIUS_PACKET *rep)
+static int rc_respond_eap_md5(rc_eap_context_t *eap_context,
+                              RADIUS_PACKET *req, RADIUS_PACKET *rep)
 {
        VALUE_PAIR *vp, *id, *state;
        size_t valuesize;
@@ -752,22 +1118,22 @@ static int respond_eap_md5(RADIUS_PACKET *req,
        FR_MD5_CTX      context;
        uint8_t    response[16];
 
-       cleanresp(rep);
+       rc_cleanresp(rep);
 
-       if ((state = paircopy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
+       if ((state = fr_pair_list_copy_by_num(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
        {
                ERROR("no state attribute found");
                return 0;
        }
 
-       if ((id = paircopy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
+       if ((id = fr_pair_list_copy_by_num(NULL, req->vps, PW_EAP_ID, 0, TAG_ANY)) == NULL)
        {
                ERROR("no EAP-ID attribute found");
                return 0;
        }
        identifier = id->vp_integer;
 
-       if ((vp = pairfind(req->vps, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL)
+       if ((vp = fr_pair_find_by_num(req->vps, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL)
        {
                ERROR("no EAP-MD5 attribute found");
                return 0;
@@ -778,7 +1144,7 @@ static int respond_eap_md5(RADIUS_PACKET *req,
        value = &vp->vp_octets[1];
 
        /* sanitize items */
-       if(valuesize > vp->vp_length)
+       if (valuesize > vp->vp_length)
        {
                ERROR("md5 valuesize if too big (%u > %u)",
                        (unsigned int) valuesize, (unsigned int) vp->vp_length);
@@ -791,7 +1157,7 @@ static int respond_eap_md5(RADIUS_PACKET *req,
         */
        fr_md5_init(&context);
        fr_md5_update(&context, &identifier, 1);
-       fr_md5_update(&context, (uint8_t *) password, strlen(password));
+       fr_md5_update(&context, (uint8_t *) eap_context->password, strlen(eap_context->password));
        fr_md5_update(&context, value, valuesize);
        fr_md5_final(response, &context);
 
@@ -799,158 +1165,296 @@ static int respond_eap_md5(RADIUS_PACKET *req,
                uint8_t *p;
                uint8_t lg_response;
 
-               vp = paircreate(rep, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0);
+               vp = fr_pair_afrom_num(rep, PW_EAP_TYPE_BASE+PW_EAP_MD5, 0);
                vp->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);
+               fr_pair_value_memsteal(vp, p);
        }
-       pairreplace(&(rep->vps), vp);
+       fr_pair_replace(&(rep->vps), vp);
 
-       pairreplace(&(rep->vps), id);
+       fr_pair_replace(&(rep->vps), id);
 
        /* copy the state object in */
-       pairreplace(&(rep->vps), state);
+       fr_pair_replace(&(rep->vps), state);
 
        return 1;
 }
 
 
-
-static int sendrecv_eap(RADIUS_PACKET *rep)
+/** Allocate a new socket, and add it to the packet list.
+ */
+static void rc_add_socket(fr_ipaddr_t *src_ipaddr, uint16_t src_port, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
 {
-       RADIUS_PACKET *req = NULL;
-       VALUE_PAIR *vp, *vpnext;
-       int tried_eap_md5 = 0;
+       int mysockfd;
 
-       if (!rep) return -1;
+       /* Trace what we're doing. */
+       char src_addr[15+1] = "";
+       char dst_addr[15+1] = "";
+       inet_ntop(AF_INET, &(src_ipaddr->ipaddr.ip4addr.s_addr), src_addr, sizeof(src_addr));
+       inet_ntop(AF_INET, &(dst_ipaddr->ipaddr.ip4addr.s_addr), dst_addr, sizeof(dst_addr));
 
-       /*
-        *      Keep a copy of the the User-Password attribute.
-        */
-       if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
-               strlcpy(password, vp->vp_strvalue, sizeof(password));
+       INFO("Adding new socket: src: %s:%d, dst: %s:%d", src_addr, src_port, dst_addr, dst_port);
 
-       } else  if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
-               strlcpy(password, vp->vp_strvalue, sizeof(password));
-               /*
-                *      Otherwise keep a copy of the CHAP-Password attribute.
-                */
-       } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
-               strlcpy(password, vp->vp_strvalue, sizeof(password));
-       } else {
-               *password = '\0';
+       mysockfd = fr_socket(src_ipaddr, src_port);
+       if (mysockfd < 0) {
+               ERROR("Failed to create new socket: %s", fr_strerror());
+               exit(1);
        }
 
- again:
-       rep->id++;
+       if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, dst_ipaddr, dst_port, NULL)) {
+               ERROR("Failed to add new socket: %s", fr_strerror());
+               exit(1);
+       }
 
-       /*
-        * if there are EAP types, encode them into an EAP-Message
-        *
-        */
-       map_eap_methods(rep);
+       num_sockets ++;
+       DEBUG("Added new socket: %d (num sockets: %d)", mysockfd, num_sockets);
+}
 
-       /*
-        *  Fix up Digest-Attributes issues
-        */
-       for (vp = rep->vps; vp != NULL; vp = vp->next) {
-               switch (vp->da->attr) {
-               default:
-                       break;
+/** Send one packet for a transaction.
+ */
+static int rc_send_one_packet(rc_transaction_t *trans, RADIUS_PACKET **packet_p)
+{
+       if (!trans || !packet_p || !*packet_p) return -1;
 
-               case PW_DIGEST_REALM:
-               case PW_DIGEST_NONCE:
-               case PW_DIGEST_METHOD:
-               case PW_DIGEST_URI:
-               case PW_DIGEST_QOP:
-               case PW_DIGEST_ALGORITHM:
-               case PW_DIGEST_BODY_DIGEST:
-               case PW_DIGEST_CNONCE:
-               case PW_DIGEST_NONCE_COUNT:
-               case PW_DIGEST_USER_NAME:
-               /* overlapping! */
-               {
-                       DICT_ATTR const *da;
-                       uint8_t *p, *q;
+       assert(pl != NULL);
 
-                       p = talloc_array(vp, uint8_t, vp->vp_length + 2);
+       RADIUS_PACKET *packet = *packet_p;
 
-                       memcpy(p + 2, vp->vp_octets, vp->vp_length);
-                       p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
-                       vp->vp_length += 2;
-                       p[1] = vp->vp_length;
+       if (packet->id == -1) {
+               /* Haven't sent the packet yet.  Initialize it. */
+               bool rcode;
+               int i;
 
-                       da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
-                       vp->da = da;
+               rc_build_eap_context(trans); /* In case of EAP, build EAP-Message and initialize EAP context. */
 
-                       /*
-                        *      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);
+               assert(trans->reply == NULL);
 
-                       vp->vp_octets = talloc_steal(vp, p);
-                       vp->type = VT_DATA;
+               trans->tries = 0;
+               packet->src_ipaddr.af = server_ipaddr.af;
+               int nb_sock_add = 0;
+               while (1) {
+                       /* Allocate a RADIUS packet ID from a suitable socket of the packet list. */
+                       rcode = fr_packet_list_id_alloc(pl, ipproto, packet_p, NULL);
 
-                       VERIFY_VP(vp);
+                       if (rcode) { /* Got an ID. */
+                               break;
+                       }
+                       if (nb_sock_add >= 1) {
+                               ERROR("Added %d new socket(s), but still could not get an ID (currently: %d outgoing requests).",
+                                       nb_sock_add, fr_packet_list_num_outgoing(pl));
+                               exit(1);
+                       }
+
+                       /* Could not find a free packet ID. Allocate a new socket, then try again. */
+                       rc_add_socket(&packet->src_ipaddr, packet->src_port, &packet->dst_ipaddr, packet->dst_port);
+
+                       nb_sock_add ++;
                }
-                       break;
+
+               assert(packet->id != -1);
+               assert(packet->data == NULL);
+
+               for (i = 0; i < 4; i++) {
+                       ((uint32_t *) packet->vector)[i] = fr_rand();
                }
        }
 
        /*
-        *      If we've already sent a packet, free up the old
-        *      one, and ensure that the next packet has a unique
-        *      ID and authentication vector.
+        *      Send the packet.
         */
-       if (rep->data) {
-               talloc_free(rep->data);
-               rep->data = NULL;
+       DEBUG("Transaction: %u, sending packet: %u (id: %u)...", trans->id, trans->num_packet, packet->id);
+
+       gettimeofday(&packet->timestamp, NULL); /* set outgoing packet timestamp. */
+
+       if (rad_send(packet, NULL, secret) < 0) {
+               ERROR("Failed to send packet (sockfd: %d, id: %d): %s",
+                       packet->sockfd, packet->id, fr_strerror());
        }
 
-       fr_md5_calc(rep->vector, rep->vector, sizeof(rep->vector));
+       trans->num_packet ++;
+       trans->tries ++;
 
-       if (*password != '\0') {
-               if ((vp = pairfind(rep->vps, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) != NULL) {
-                       pairstrcpy(vp, password);
+       if (fr_debug_lvl > 0) fr_packet_header_print(fr_log_fp, packet, false);
+       if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, packet->vps);
 
-               } else if ((vp = pairfind(rep->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
-                       pairstrcpy(vp, password);
+       return 1;
+}
+
+/** Send current packet of a transaction. Arm timeout event.
+ */
+static int rc_send_transaction_packet(rc_transaction_t *trans, RADIUS_PACKET **packet_p)
+// note: we need a 'RADIUS_PACKET **' for fr_packet_list_id_alloc.
+{
+       if (!trans || !packet_p || !*packet_p) return -1;
 
-               } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
-                       pairstrcpy(vp, password);
+       int ret = rc_send_one_packet(trans, packet_p);
+       if (ret == 1) {
+               /* Send successful: arm the timeout callback. */
+               rc_evprep_packet_timeout(trans);
+       }
+       return ret;
+}
 
-                       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 */
+/** Deallocate RADIUS packet ID.
+ */
+static void rc_deallocate_id(rc_transaction_t *trans)
+{
+       if (!trans || !trans->packet ||
+           (trans->packet->id < 0)) {
+               return;
+       }
+
+       RADIUS_PACKET *packet = trans->packet;
+
+       DEBUG2("Deallocating (sockfd: %d, id: %d)", packet->sockfd, packet->id);
 
-       /* send the response, wait for the next request */
-       send_packet(rep, &req);
-       if (!req) {
-               ERROR("Failed getting response (EAP-Request from server)");
+       /*
+        *      One more unused RADIUS ID.
+        */
+       fr_packet_list_id_free(pl, packet, true);
+       /* note: "true" means automatically yank, so we must *not* yank ourselves before calling (otherwise, it does nothing)
+        * so, *don't*: fr_packet_list_yank(pl, request->packet); */
+
+       /* free more stuff to ensure next allocate won't be stuck on a "full" socket. */
+       packet->id = -1;
+       packet->sockfd = -1;
+       packet->src_ipaddr.af = AF_UNSPEC;
+       packet->src_port = 0;
+
+       /*
+        *      If we've already sent a packet, free up the old one,
+        *      and ensure that the next packet has a unique
+        *      authentication vector.
+        */
+       if (packet->data) {
+               talloc_free(packet->data);
+               packet->data = NULL;
+       }
+
+       if (trans->reply) rad_free(&trans->reply);
+}
+
+/** Receive one packet, maybe.
+ */
+static int rc_recv_one_packet(struct timeval *tv_wait_time)
+{
+       fd_set set;
+       struct timeval tv;
+       rc_transaction_t *trans;
+       RADIUS_PACKET *reply, **packet_p;
+       volatile int max_fd;
+       bool ongoing_trans = false;
+       char buffer[128];
+
+       /* Wait for reply, timing out as necessary */
+       FD_ZERO(&set);
+
+       max_fd = fr_packet_list_fd_set(pl, &set);
+       if (max_fd < 0) {
+               /* no sockets to listen on! */
+               return 0;
+       }
+
+       if (NULL == tv_wait_time) {
+               timerclear(&tv);
+       } else {
+               tv.tv_sec = tv_wait_time->tv_sec;
+               tv.tv_usec = tv_wait_time->tv_usec;
+       }
+
+       if (select(max_fd, &set, NULL, NULL, &tv) <= 0) {
+               /* No packet was received. */
+               return 0;
+       }
+
+       /*
+        *      Receive the reply.
+        */
+       reply = fr_packet_list_recv(pl, &set);
+       if (!reply) {
+               ERROR("Received bad packet: %s", fr_strerror());
+               return -1;      /* bad packet */
+       }
+
+       /*
+        *      Look for the packet which matches the reply.
+        */
+       reply->src_ipaddr = server_ipaddr;
+       reply->src_port = server_port;
+
+       /*
+        * Note: this only works if all packets have the same destination (IP, port).
+        * We should handle a list of destinations. But we don't. radclient doesn't do it either).
+        */
+
+       packet_p = fr_packet_list_find_byreply(pl, reply);
+
+       if (!packet_p) {
+               /* got reply to packet we didn't send.
+                * (or maybe we sent it, got no response, freed the ID. Then server responds to first request.)
+                */
+               DEBUG("No outstanding request was found for reply from %s, port %d (sockfd: %d, id: %d)",
+                       inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.ipaddr, buffer, sizeof(buffer)),
+                       reply->src_port, reply->sockfd, reply->id);
+               rad_free(&reply);
                return -1;
        }
 
-       /* okay got back the packet, go and decode the EAP-Message. */
-       unmap_eap_methods(req);
+       trans = fr_packet2myptr(rc_transaction_t, packet, packet_p);
+
+       if (trans->event) fr_event_delete(ev_list, &trans->event);
+
+       /*
+        *      Fails the signature validation: not a valid reply.
+        */
+       if (rad_verify(reply, trans->packet, secret) < 0) {
+               /* shared secret is incorrect.
+                * (or maybe this is a response to another packet we sent, for which we got no response,
+                * freed the ID, then reused it. Then server responds to first packet.)
+                */
+               DEBUG("Conflicting response authenticator for reply from %s (sockfd: %d, id: %d)",
+                       inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.ipaddr, buffer, sizeof(buffer)),
+                       reply->sockfd, reply->id);
+
+               goto packet_done;
+       }
+
+       /* Set reply destination = packet source. */
+       reply->dst_ipaddr = trans->packet->src_ipaddr;
+       reply->dst_port = trans->packet->src_port;
+
+       trans->reply = reply;
+       reply = NULL;
+
+       if (rad_decode(trans->reply, trans->packet, secret) != 0) {
+               /* This can fail if packet contains too many attributes. */
+               DEBUG("Failed decoding reply");
+               goto packet_done;
+       }
 
-       debug_packet(req, R_RECV);
+       gettimeofday(&trans->reply->timestamp, NULL); /* set received packet timestamp. */
+
+       if (trans->eap_context) {
+               /* Call unmap before packet print (so we can see the decoded EAP stuff). */
+               rc_unmap_eap_methods(trans->reply);
+       }
+
+       DEBUG("Transaction: %u, received packet (id: %u).", trans->id, trans->reply->id);
+
+       if (fr_debug_lvl > 0) fr_packet_header_print(fr_log_fp, trans->reply, true);
+       if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, trans->reply->vps);
+
+       if (!trans->eap_context) {
+               goto packet_done;
+       }
 
        /* now look for the code type. */
-       for (vp = req->vps; vp != NULL; vp = vpnext) {
+       VALUE_PAIR *vp, *vpnext;
+       for (vp = trans->reply->vps; vp != NULL; vp = vpnext) {
                vpnext = vp->next;
 
                switch (vp->da->attr) {
@@ -958,23 +1462,178 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
                        break;
 
                case PW_EAP_TYPE_BASE + PW_EAP_MD5:
-                       if (respond_eap_md5(req, rep) && tried_eap_md5 < 3) {
-                               tried_eap_md5++;
-                               goto again;
+                       if (rc_respond_eap_md5(trans->eap_context, trans->reply, trans->packet) && trans->eap_context->eap.md5.tried < 3)
+                       {
+                               /* answer the challenge from server. */
+                               trans->eap_context->eap.md5.tried ++;
+                               rc_deallocate_id(trans);
+                               rc_send_transaction_packet(trans, &trans->packet);
+                               ongoing_trans = true; // don't free the transaction yet.
                        }
-                       break;
+                       goto packet_done;
 
                case PW_EAP_TYPE_BASE + PW_EAP_SIM:
-                       if (respond_eap_sim(req, rep)) {
-                               goto again;
+                       if (rc_respond_eap_sim(trans->eap_context, trans->reply, trans->packet)) {
+                               /* answer the challenge from server. */
+                               rc_deallocate_id(trans);
+                               rc_send_transaction_packet(trans, &trans->packet);
+                               ongoing_trans = true; // don't free the transaction yet.
                        }
-                       break;
+                       goto packet_done;
                }
        }
 
+       goto eap_done;
+
+eap_done:
+       /* EAP transaction ends here (no more requests from EAP server). */
+
+       /*
+        * success: if we have EAP-Code = Success, and reply is an Access-Accept.
+        */
+       if (trans->reply->code != PW_CODE_ACCESS_ACCEPT) {
+               DEBUG("EAP transaction finished, but reply is not an Access-Accept");
+               goto packet_done;
+       }
+       vp = fr_pair_find_by_num(trans->reply->vps, PW_EAP_CODE, 0, TAG_ANY);
+       if ( (!vp) || (vp->vp_integer != 3) ) {
+               DEBUG("EAP transaction finished, but reply does not contain EAP-Code = Success");
+               goto packet_done;
+       }
+       goto packet_done;
+
+packet_done:
+
+       /* Basic statistics (salvaged from old code). TODO: something better. */
+       if (trans->reply) {
+               if (trans->reply->code == PW_CODE_ACCESS_ACCEPT) {
+                       totalapp ++;
+               } else if (trans->reply->code == PW_CODE_ACCESS_REJECT) {
+                       totaldeny ++;
+               }
+       }
+
+       rad_free(&trans->reply);
+       rad_free(&reply);       /* may be NULL */
+
+       if (!ongoing_trans) {
+               rc_deallocate_id(trans);
+               rc_finish_transaction(trans);
+       }
+
        return 1;
 }
 
+/** Event callback: packet timeout.
+ */
+static void rc_evcb_packet_timeout(void *ctx)
+{
+       rc_transaction_t *trans = ctx;
+       if (!trans || !trans->packet) return;
+
+       DEBUG("Timeout for transaction: %d, tries (so far): %d (max: %d)", trans->id, trans->tries, retries);
+
+       if (trans->event) fr_event_delete(ev_list, &trans->event);
+
+       if (trans->tries < retries) {
+               /* Try again. */
+               rc_send_transaction_packet(trans, &trans->packet);
+       } else {
+               DEBUG("No response for transaction: %d, giving up", trans->id);
+               rc_finish_transaction(trans);
+       }
+}
+
+/** Prepare event: packet timeout.
+ */
+static void rc_evprep_packet_timeout(rc_transaction_t *trans)
+{
+       struct timeval tv_event;
+       gettimeofday(&tv_event, NULL);
+       timeradd(&tv_event, &tv_timeout, &tv_event);
+
+       if (!fr_event_insert(ev_list, rc_evcb_packet_timeout, (void *)trans, &tv_event, &trans->event)) {
+               ERROR("Failed to insert event");
+               exit(1);
+       }
+}
+
+/** Trigger all armed events for which time is reached.
+ */
+static int rc_loop_events(void)
+{
+       struct timeval when;
+       uint32_t nb_processed = 0;
+
+       if (!fr_event_list_num_elements(ev_list)) return 0;
+
+       while (1) {
+               gettimeofday(&when, NULL);
+               if (!fr_event_run(ev_list, &when)) {
+                       /* no more. */
+                       break;
+               }
+               nb_processed ++;
+       }
+       return nb_processed;
+}
+
+/** Receive loop.
+ *  Handle incoming packets, until nothing more is received.
+ */
+static int dhb_loop_recv(void)
+{
+       uint32_t nb_received = 0;
+       while (rc_recv_one_packet(NULL) > 0) {
+               nb_received ++;
+       }
+       return nb_received;
+}
+
+/** Loop starting new transactions, until a limit is reached
+ *  (max parallelism, or no more input available.)
+ */
+static int rc_loop_start_transactions(void)
+{
+       int nb_started = 0;
+
+       while (1) {
+               if (num_ongoing >= parallel) break;
+
+               /* Try to initialize a new transaction. */
+               rc_transaction_t *trans = rc_init_transaction(autofree);
+               if (!trans) break;
+
+               nb_started ++;
+               rc_send_transaction_packet(trans, &trans->packet);
+       }
+       return nb_started;
+}
+
+/** Main loop: Handle events. Receive and process responses. Start new transactions.
+ *  Until we're done.
+ */
+static void rc_main_loop(void)
+{
+       while (1) {
+               /* Handle events. */
+               rc_loop_events();
+
+               /* Receive and process response until no more are received (don't wait). */
+               dhb_loop_recv();
+
+               /* Start new transactions and send the associated packet. */
+               rc_loop_start_transactions();
+
+               /* Check if we're done. */
+               if ( (rc_vps_list_in.size == 0)
+                       && (fr_packet_list_num_outgoing(pl) == 0) ) {
+                       break;
+               }
+       }
+       INFO("Main loop: done.");
+}
+
 
 void set_radius_dir(TALLOC_CTX *ctx, char const *path)
 {
@@ -989,16 +1648,117 @@ void set_radius_dir(TALLOC_CTX *ctx, char const *path)
 }
 
 
+/** Set a port from the request type if we don't already have one.
+ */
+static void rc_get_port(PW_CODE type, uint16_t *port)
+{
+       switch (type) {
+       default:
+       case PW_CODE_ACCESS_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:
+               if (*port == 0) *port = PW_POD_UDP_PORT;
+               return;
+
+       case PW_CODE_COA_REQUEST:
+               if (*port == 0) *port = PW_COA_UDP_PORT;
+               return;
+
+       case PW_CODE_UNDEFINED:
+               if (*port == 0) *port = 0;
+               return;
+       }
+}
+
+/** Resolve a port to a request type.
+ */
+static PW_CODE rc_get_code(uint16_t port)
+{
+       /*
+        *      getport returns 0 if the service doesn't exist
+        *      so we need to return early, to avoid incorrect
+        *      codes.
+        */
+       if (port == 0) return PW_CODE_UNDEFINED;
+
+       if ((port == getport("radius")) || (port == PW_AUTH_UDP_PORT) || (port == PW_AUTH_UDP_PORT_ALT)) {
+               return PW_CODE_ACCESS_REQUEST;
+       }
+       if ((port == getport("radacct")) || (port == PW_ACCT_UDP_PORT) || (port == PW_ACCT_UDP_PORT_ALT)) {
+               return PW_CODE_ACCOUNTING_REQUEST;
+       }
+       if (port == PW_COA_UDP_PORT) return PW_CODE_COA_REQUEST;
+       if (port == PW_POD_UDP_PORT) return PW_CODE_DISCONNECT_REQUEST;
+
+       return PW_CODE_UNDEFINED;
+}
+
+/** Resolve server hostname.
+ */
+static void rc_resolve_hostname(char *server_arg)
+{
+       if (force_af == AF_UNSPEC) force_af = AF_INET;
+       server_ipaddr.af = force_af;
+       if (strcmp(server_arg, "-") != 0) {
+               char *p;
+               char const *hostname = server_arg;
+               char const *portname = server_arg;
+               char buffer[256];
+
+               if (*server_arg == '[') { /* IPv6 URL encoded */
+                       p = strchr(server_arg, ']');
+                       if ((size_t) (p - server_arg) >= sizeof(buffer)) {
+                               usage();
+                       }
+
+                       memcpy(buffer, server_arg + 1, p - server_arg - 1);
+                       buffer[p - server_arg - 1] = '\0';
+
+                       hostname = buffer;
+                       portname = p + 1;
+
+               }
+               p = strchr(portname, ':');
+               if (p && (strchr(p + 1, ':') == NULL)) {
+                       *p = '\0';
+                       portname = p + 1;
+               } else {
+                       portname = NULL;
+               }
+
+               if (ip_hton(&server_ipaddr, force_af, hostname, false) < 0) {
+                       ERROR("%s: Failed to find IP address for host %s: %s", progname, hostname, strerror(errno));
+                       exit(1);
+               }
+               server_addr_init = true;
+
+               /* Strip port from hostname if needed. */
+               if (portname) server_port = atoi(portname);
+
+               /*
+                *      Work backwards from the port to determine the packet type
+                */
+               if (packet_code == PW_CODE_UNDEFINED) packet_code = rc_get_code(server_port);
+       }
+       rc_get_port(packet_code, &server_port);
+}
+
 int main(int argc, char **argv)
 {
-       RADIUS_PACKET *req;
        char *p;
        int c;
-       uint16_t port = 0;
        char *filename = NULL;
        FILE *fp;
-       int id;
-       int force_af = AF_UNSPEC;
 
        static fr_log_t radclient_log = {
                .colourise = true,
@@ -1015,14 +1775,14 @@ int main(int argc, char **argv)
         *      directly, so we'll allocate a new context beneath it, and
         *      free that before any leak reports.
         */
-       TALLOC_CTX *autofree = talloc_init("main");
+       autofree = talloc_init("main");
 
-       id = ((int)getpid() & 0xff);
-       fr_debug_flag = 0;
+       fr_debug_lvl = 0;
+       fr_log_fp = stdout;
 
        set_radius_dir(autofree, RADIUS_DIR);
 
-       while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF)
+       while ((c = getopt(argc, argv, "46c:d:D:f:hp:qst:r:S:xXv")) != EOF)
        {
                switch (c) {
                case '4':
@@ -1040,12 +1800,17 @@ int main(int argc, char **argv)
                case 'f':
                        filename = optarg;
                        break;
+               case 'p':
+                       parallel = atoi(optarg);
+                       if (parallel == 0) parallel = 1;
+                       if (parallel > 65536) parallel = 65536;
+                       break;
                case 'q':
                        do_output = 0;
                        break;
                case 'x':
-                       debug_flag++;
-                       fr_debug_flag++;
+                       rad_debug_lvl++;
+                       fr_debug_lvl++;
                        break;
 
                case 'X':
@@ -1054,21 +1819,11 @@ int main(int argc, char **argv)
 #endif
                  break;
 
-
-
                case 'r':
                        if (!isdigit((int) *optarg))
                                usage();
                        retries = atoi(optarg);
                        break;
-               case 'i':
-                       if (!isdigit((int) *optarg))
-                               usage();
-                       id = atoi(optarg);
-                       if ((id < 0) || (id > 255)) {
-                               usage();
-                       }
-                       break;
                case 's':
                        do_summary = 1;
                        break;
@@ -1081,7 +1836,7 @@ int main(int argc, char **argv)
                        printf("$Id$ built on "__DATE__ "at "__TIME__ "");
                        exit(0);
 
-              case 'S':
+               case 'S':
                       fp = fopen(optarg, "r");
                       if (!fp) {
                               ERROR("Error opening %s: %s",
@@ -1122,6 +1877,8 @@ int main(int argc, char **argv)
                usage();
        }
 
+       /* Prepare the timeout. */
+       rc_float_to_timeval(&tv_timeout, timeout);
 
        if (!main_config.dictionary_dir) {
                main_config.dictionary_dir = DICTDIR;
@@ -1155,86 +1912,23 @@ int main(int argc, char **argv)
                DEBUG2("Including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
        }
 
-       req = rad_alloc(NULL, true);
-       if (!req) {
-               ERROR("%s", fr_strerror());
-               exit(1);
-       }
-       req->id = id;
-
        /*
-        *      Resolve hostname.
+        *      Get the request type
         */
-       if (force_af == AF_UNSPEC) force_af = AF_INET;
-       req->dst_ipaddr.af = force_af;
-       if (strcmp(argv[1], "-") != 0) {
-               char const *hostname = argv[1];
-               char const *portname = argv[1];
-               char buffer[256];
-
-               if (*argv[1] == '[') { /* IPv6 URL encoded */
-                       p = strchr(argv[1], ']');
-                       if ((size_t) (p - argv[1]) >= sizeof(buffer)) {
-                               usage();
-                       }
-
-                       memcpy(buffer, argv[1] + 1, p - argv[1] - 1);
-                       buffer[p - argv[1] - 1] = '\0';
-
-                       hostname = buffer;
-                       portname = p + 1;
-
-               }
-               p = strchr(portname, ':');
-               if (p && (strchr(p + 1, ':') == NULL)) {
-                       *p = '\0';
-                       portname = p + 1;
-               } else {
-                       portname = NULL;
-               }
-
-               if (ip_hton(&req->dst_ipaddr, force_af, hostname, false) < 0) {
-                       ERROR("Failed to find IP address for host %s: %s", hostname, fr_syserror(errno));
-                       exit(1);
+       if (!isdigit((int) argv[2][0])) {
+               packet_code = fr_str2int(rc_request_types, argv[2], -2);
+               if (packet_code == -2) {
+                       ERROR("Unrecognised request type \"%s\"", argv[2]);
+                       usage();
                }
-
-               /*
-                *      Strip port from hostname if needed.
-                */
-               if (portname) port = atoi(portname);
+       } else {
+               packet_code = atoi(argv[2]);
        }
 
        /*
-        *      See what kind of request we want to send.
+        *      Resolve hostname.
         */
-       if (strcmp(argv[2], "auth") == 0) {
-               if (port == 0) port = getport("radius");
-               if (port == 0) port = PW_AUTH_UDP_PORT;
-               req->code = PW_CODE_ACCESS_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_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_CODE_STATUS_SERVER;
-
-       } else if (strcmp(argv[2], "disconnect") == 0) {
-               if (port == 0) port = PW_POD_UDP_PORT;
-               req->code = PW_CODE_DISCONNECT_REQUEST;
-
-       } else if (isdigit((int) argv[2][0])) {
-               if (port == 0) port = getport("radius");
-               if (port == 0) port = PW_AUTH_UDP_PORT;
-               req->code = atoi(argv[2]);
-       } else {
-               usage();
-       }
-       req->dst_port = port;
+       rc_resolve_hostname(argv[1]);
 
        /*
         *      Add the secret.
@@ -1242,37 +1936,30 @@ int main(int argc, char **argv)
        if (argv[3]) secret = argv[3];
 
        /*
-        *      Read valuepairs.
-        *      Maybe read them, from stdin, if there's no
-        *      filename, or if the filename is '-'.
+        *      Read input data vp(s) from the file (or stdin).
         */
-       if (filename && (strcmp(filename, "-") != 0)) {
-               fp = fopen(filename, "r");
-               if (!fp) {
-                       ERROR("Error opening %s: %s", filename, fr_syserror(errno));
-                       exit(1);
-               }
-       } else {
-               fp = stdin;
+       INFO("Loading input data...");
+       if (!rc_load_input(autofree, filename, &rc_vps_list_in, 0)
+           || rc_vps_list_in.size == 0) {
+               ERROR("No valid input. Nothing to send.");
+               exit(EXIT_FAILURE);
        }
+       INFO("Loaded: %d input element(s).", rc_vps_list_in.size);
 
-       /*
-        *      Send request.
-        */
-       if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-               ERROR("socket: %s", fr_syserror(errno));
+       /* Initialize the packets list. */
+       MEM(pl = fr_packet_list_create(1));
+
+       /* Initialize the events list. */
+       ev_list = fr_event_list_create(autofree, NULL);
+       if (!ev_list) {
+               ERROR("Failed to create event list");
                exit(1);
        }
 
-       while (!filedone) {
-               if (req->vps) pairfree(&req->vps);
-               if (readvp2(NULL, &req->vps, fp, &filedone) < 0) {
-                       ERROR("%s", fr_strerror());
-                       break;
-               }
-
-               sendrecv_eap(req);
-       }
+       /*
+        *      Start main loop.
+        */
+       rc_main_loop();
 
        if (do_summary) {
                INFO("\n\t   Total approved auths:  %d", totalapp);
@@ -1284,52 +1971,46 @@ int main(int argc, char **argv)
        return 0;
 }
 
-/*
- * given a radius request with some attributes in the EAP range, build
- * them all into a single EAP-Message body.
- *
- * Note that this function will build multiple EAP-Message bodies
- * if there are multiple eligible EAP-types. This is incorrect, as the
- * recipient will in fact concatenate them.
- *
- * XXX - we could break the loop once we process one type. Maybe this
- *       just deserves an assert?
+/** Given a radius request with some attributes in the EAP range, build
+ *  them all into a single EAP-Message body.
  *
+ *  If there are multiple eligibles EAP-Type, the first one is picked.
+ *  Function returns 0 if no EAP is involved, or the EAP-Type otherwise.
  */
-static void map_eap_methods(RADIUS_PACKET *req)
+static int rc_map_eap_methods(RADIUS_PACKET *req)
 {
        VALUE_PAIR *vp, *vpnext;
        int id, eapcode;
-       int eap_method;
+       int eap_method = 0;
 
        eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t);
 
-       vp = pairfind(req->vps, PW_EAP_ID, 0, TAG_ANY);
-       if(!vp) {
+       vp = fr_pair_find_by_num(req->vps, PW_EAP_ID, 0, TAG_ANY);
+       if (!vp) {
                id = ((int)getpid() & 0xff);
        } else {
                id = vp->vp_integer;
        }
 
-       vp = pairfind(req->vps, PW_EAP_CODE, 0, TAG_ANY);
-       if(!vp) {
+       vp = fr_pair_find_by_num(req->vps, PW_EAP_CODE, 0, TAG_ANY);
+       if (!vp) {
                eapcode = PW_EAP_REQUEST;
        } else {
                eapcode = vp->vp_integer;
        }
 
-       for(vp = req->vps; vp != NULL; vp = vpnext) {
+       for (vp = req->vps; vp != NULL; vp = vpnext) {
                /* save it in case it changes! */
                vpnext = vp->next;
 
-               if(vp->da->attr >= PW_EAP_TYPE_BASE &&
+               if (vp->da->attr >= PW_EAP_TYPE_BASE &&
                   vp->da->attr < PW_EAP_TYPE_BASE+256) {
                        break;
                }
        }
 
-       if(!vp) {
-               return;
+       if (!vp) {
+               return 0;
        }
 
        eap_method = vp->da->attr - PW_EAP_TYPE_BASE;
@@ -1352,7 +2033,7 @@ static void map_eap_methods(RADIUS_PACKET *req)
                 */
 
                /* nuke any existing EAP-Messages */
-               pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+               fr_pair_delete_by_num(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
 
                pt_ep->code = eapcode;
                pt_ep->id = id;
@@ -1364,13 +2045,15 @@ static void map_eap_methods(RADIUS_PACKET *req)
 
                eap_basic_compose(req, pt_ep);
        }
+
+       return eap_method;
 }
 
 /*
  * given a radius request with an EAP-Message body, decode it specific
  * attributes.
  */
-static void unmap_eap_methods(RADIUS_PACKET *rep)
+static void rc_unmap_eap_methods(RADIUS_PACKET *rep)
 {
        VALUE_PAIR *eap1;
        eap_packet_raw_t *e;
@@ -1386,13 +2069,13 @@ static void unmap_eap_methods(RADIUS_PACKET *rep)
                return;
        }
        /* create EAP-ID and EAP-CODE attributes to start */
-       eap1 = paircreate(rep, PW_EAP_ID, 0);
+       eap1 = fr_pair_afrom_num(rep, PW_EAP_ID, 0);
        eap1->vp_integer = e->id;
-       pairadd(&(rep->vps), eap1);
+       fr_pair_add(&(rep->vps), eap1);
 
-       eap1 = paircreate(rep, PW_EAP_CODE, 0);
+       eap1 = fr_pair_afrom_num(rep, PW_EAP_CODE, 0);
        eap1->vp_integer = e->code;
-       pairadd(&(rep->vps), eap1);
+       fr_pair_add(&(rep->vps), eap1);
 
        switch (e->code) {
        default:
@@ -1413,7 +2096,7 @@ static void unmap_eap_methods(RADIUS_PACKET *rep)
                len = e->length[0]*256 + e->length[1];
 
                /* verify the length is big enough to hold type */
-               if(len < 5)
+               if (len < 5)
                {
                        talloc_free(e);
                        return;
@@ -1428,10 +2111,10 @@ static void unmap_eap_methods(RADIUS_PACKET *rep)
                        len = MAX_STRING_LEN;
                }
 
-               eap1 = paircreate(rep, type, 0);
-               pairmemcpy(eap1, e->data + 1, len);
+               eap1 = fr_pair_afrom_num(rep, type, 0);
+               fr_pair_value_memcpy(eap1, e->data + 1, len);
 
-               pairadd(&(rep->vps), eap1);
+               fr_pair_add(&(rep->vps), eap1);
                break;
        }
 
@@ -1439,7 +2122,7 @@ static void unmap_eap_methods(RADIUS_PACKET *rep)
        return;
 }
 
-static int map_eapsim_types(RADIUS_PACKET *r)
+static int rc_map_eapsim_types(RADIUS_PACKET *r)
 {
        int ret;
 
@@ -1447,7 +2130,7 @@ static int map_eapsim_types(RADIUS_PACKET *r)
 
        ret = map_eapsim_basictypes(r, pt_ep);
 
-       if(ret != 1) {
+       if (ret != 1) {
                return ret;
        }
 
@@ -1456,13 +2139,13 @@ static int map_eapsim_types(RADIUS_PACKET *r)
        return 1;
 }
 
-static int unmap_eapsim_types(RADIUS_PACKET *r)
+static int rc_unmap_eapsim_types(RADIUS_PACKET *r)
 {
        VALUE_PAIR           *esvp;
        uint8_t *eap_data;
        int rcode_unmap;
 
-       esvp = pairfind(r->vps, PW_EAP_TYPE_BASE+PW_EAP_SIM, 0, TAG_ANY);
+       esvp = fr_pair_find_by_num(r->vps, PW_EAP_TYPE_BASE+PW_EAP_SIM, 0, TAG_ANY);
        if (!esvp) {
                ERROR("eap: EAP-Sim attribute not found");
                return 0;
@@ -1477,116 +2160,3 @@ static int unmap_eapsim_types(RADIUS_PACKET *r)
        return rcode_unmap;
 }
 
-#ifdef TEST_CASE
-
-#include <assert.h>
-
-char const *radius_dir = RADDBDIR;
-
-main(int argc, char *argv[])
-{
-       int filedone;
-       RADIUS_PACKET *req,*req2;
-       VALUE_PAIR *vp, *vpkey, *vpextra;
-       extern unsigned int sha1_data_problems;
-
-       req = NULL;
-       req2 = NULL;
-       filedone = 0;
-
-       if(argc>1) {
-         sha1_data_problems = 1;
-       }
-
-       if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
-               ERROR("%s", fr_strerror());
-               return 1;
-       }
-
-       req = rad_alloc(NULL, true)
-       if (!req) {
-               ERROR("%s", fr_strerror());
-               exit(1);
-       }
-
-       req2 = rad_alloc(NULL, true);
-       if (!req2) {
-               ERROR("%s", fr_strerror());
-               exit(1);
-       }
-
-       while(!filedone) {
-               if (req->vps) pairfree(&req->vps);
-               if (req2->vps) pairfree(&req2->vps);
-
-               if (readvp2(NULL, &req->vps, stdin, &filedone) < 0) {
-                       ERROR("%s", fr_strerror());
-                       break;
-               }
-
-               if (fr_debug_flag > 1) {
-                       DEBUG("Read:");
-                       vp_printlist(stdout, req->vps);
-               }
-
-               map_eapsim_types(req);
-               map_eap_methods(req);
-
-               if (fr_debug_flag > 1) {
-                       DEBUG("Mapped to:");
-                       vp_printlist(stdout, req->vps);
-               }
-
-               /* find the EAP-Message, copy it to req2 */
-               vp = paircopy_by_num(NULL, req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
-
-               if(!vp) continue;
-
-               pairadd(&req2->vps, vp);
-
-               /* only call unmap for sim types here */
-               unmap_eap_methods(req2);
-               unmap_eapsim_types(req2);
-
-               if (fr_debug_flag > 1) {
-                       DEBUG("Unmapped to:");
-                       vp_printlist(stdout, req2->vps);
-               }
-
-               vp = pairfind(req2->vps, PW_EAP_SIM_MAC, 0, TAG_ANY);
-               vpkey   = pairfind(req->vps, PW_EAP_SIM_KEY, 0, TAG_ANY);
-               vpextra = pairfind(req->vps, PW_EAP_SIM_EXTRA, 0, TAG_ANY);
-
-               if(vp != NULL && vpkey != NULL && vpextra!=NULL) {
-                       uint8_t calcmac[16];
-
-                       /* find the EAP-Message, copy it to req2 */
-
-                       memset(calcmac, 0, sizeof(calcmac));
-                       DEBUG("Confirming MAC...");
-                       if (eapsim_checkmac(req2->vps, vpkey->vp_strvalue,
-                                           vpextra->vp_strvalue, vpextra->length,
-                                           calcmac)) {
-                               DEBUG("succeed");
-                       } else {
-                               int i, j;
-
-                               DEBUG("calculated MAC (");
-                               for (i = 0; i < 20; i++) {
-                                       if(j==4) {
-                                               DEBUG("_");
-                                               j=0;
-                                       }
-                                       j++;
-
-                                       DEBUG("%02x", calcmac[i]);
-                               }
-                               DEBUG("did not match");
-                       }
-               }
-
-               fflush(stdout);
-       }
-}
-#endif
-
index 4193cdb..5afdcc3 100644 (file)
@@ -34,10 +34,9 @@ static const CONF_PARSER module_config[] = {
        { "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" },
+       { "cisco_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 */
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -51,18 +50,9 @@ static int mod_detach(void *instance)
 
 #ifdef HAVE_PTHREAD_H
        pthread_mutex_destroy(&(inst->session_mutex));
-       if (inst->handler_tree) pthread_mutex_destroy(&(inst->handler_mutex));
 #endif
 
        rbtree_free(inst->session_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);
 
@@ -101,17 +91,6 @@ static int eap_handler_cmp(void const *a, void const *b)
 
 
 /*
- *     Compare two handler pointers
- */
-static int eap_handler_ptr_cmp(void const *a, void const *b)
-{
-       if (a < b) return -1;
-       if (a > b) return +1;
-       return 0;
-}
-
-
-/*
  * read the config section and load all the eap authentication types present.
  */
 static int mod_instantiate(CONF_SECTION *cs, void *instance)
@@ -139,7 +118,6 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
        for(scs = cf_subsection_find_next(cs, NULL, NULL);
            scs != NULL;
            scs = cf_subsection_find_next(cs, scs, NULL)) {
-
                char const *name;
 
                name = cf_section_name1(scs);
@@ -147,6 +125,12 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
 
                if (!strcmp(name, TLS_CONFIG_SECTION))  continue;
 
+               /*
+                *      Easier sometimes than commenting out blocks,
+                *      or deleting blocks.
+                */
+               if (!strcmp(name, "disable")) continue;
+
                method = eap_name2type(name);
                if (method == PW_EAP_INVALID) {
                        cf_log_err_cs(cs, "No dictionary definition for EAP method %s", name);
@@ -183,7 +167,7 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
                /*
                 *      Load the type.
                 */
-               ret = eap_module_load(inst, &inst->methods[method], method, scs);
+               ret = eap_module_instantiate(inst, &inst->methods[method], method, scs);
 
                (void) talloc_get_type_abort(inst->methods[method], eap_module_t);
 
@@ -234,22 +218,6 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
        }
        fr_link_talloc_ctx_free(inst, inst->session_tree);
 
-       if (fr_debug_flag) {
-               inst->handler_tree = rbtree_create(NULL, eap_handler_ptr_cmp, NULL, 0);
-               if (!inst->handler_tree) {
-                       ERROR("rlm_eap (%s): Cannot initialize tree", inst->xlat_name);
-                       return -1;
-               }
-               fr_link_talloc_ctx_free(inst, inst->handler_tree);
-
-#ifdef HAVE_PTHREAD_H
-               if (pthread_mutex_init(&(inst->handler_mutex), NULL) < 0) {
-                       ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, fr_syserror(errno));
-                       return -1;
-               }
-#endif
-       }
-
 #ifdef HAVE_PTHREAD_H
        if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) {
                ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, fr_syserror(errno));
@@ -274,7 +242,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
 
        inst = (rlm_eap_t *) instance;
 
-       if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
+       if (!fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
                REDEBUG("You set 'Auth-Type = EAP' for a request that does "
                        "not contain an EAP-Message attribute!");
                return RLM_MODULE_INVALID;
@@ -360,11 +328,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                 *      Some simple sanity checks.  These should really
                 *      be handled by the radius library...
                 */
-               vp = pairfind(request->proxy->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->proxy->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
                if (vp) {
-                       vp = pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+                       vp = fr_pair_find_by_num(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
                        if (!vp) {
-                               pairmake(request->proxy,
+                               fr_pair_make(request->proxy,
                                         &request->proxy->vps,
                                         "Message-Authenticator",
                                         NULL, T_OP_EQ);
@@ -376,7 +344,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                 *      set to 127.0.0.1 for tunneled requests, and
                 *      we don't want to tell the world that...
                 */
-               pairdelete(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS, TAG_ANY);
+               fr_pair_delete_by_num(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS, TAG_ANY);
 
                RDEBUG2("Tunneled session will be proxied.  Not doing EAP");
                return RLM_MODULE_HANDLED;
@@ -444,10 +412,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                /*
                 *      Doesn't exist, add it in.
                 */
-               vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!vp) {
-                       vp = paircopyvp(request->reply, request->username);
-                       pairadd(&request->reply->vps, vp);
+                       vp = fr_pair_copy(request->reply, request->username);
+                       fr_pair_add(&request->reply->vps, vp);
                }
 
                /*
@@ -523,9 +491,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         *      each EAP sub-module to look for handler->request->username,
         *      and to get excited if it doesn't appear.
         */
-       vp = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
-       if ((!vp) || (vp->vp_integer != PW_AUTHTYPE_REJECT)) {
-               vp = pairmake_config("Auth-Type", inst->xlat_name, T_OP_EQ);
+       vp = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
+       if ((!vp) || (vp->vp_integer != PW_AUTH_TYPE_REJECT)) {
+               vp = pair_make_config("Auth-Type", inst->xlat_name, T_OP_EQ);
                if (!vp) {
                        RDEBUG2("Failed to create Auth-Type %s: %s\n",
                                inst->xlat_name, fr_strerror());
@@ -623,9 +591,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
                        /*
                         *      Doesn't exist, add it in.
                         */
-                       vp = pairfind(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+                       vp = fr_pair_find_by_num(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
                        if (!vp) {
-                               pairmake_reply("User-Name",
+                               pair_make_reply("User-Name",
                                               request->username->vp_strvalue,
                                               T_OP_EQ);
                        }
@@ -704,7 +672,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
        rad_tunnel_pwencode(p + 17, &len,
                            request->client->secret,
                            request->packet->vector);
-       pairstrsteal(vp, p);
+       fr_pair_value_strsteal(vp, p);
 
        return RLM_MODULE_UPDATED;
 }
@@ -720,17 +688,17 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        /*
         * Only build a failure message if something previously rejected the request
         */
-       vp = pairfind(request->config_items, PW_POSTAUTHTYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
 
-       if (!vp || (vp->vp_integer != PW_POSTAUTHTYPE_REJECT)) return RLM_MODULE_NOOP;
+       if (!vp || (vp->vp_integer != PW_POST_AUTH_TYPE_REJECT)) return RLM_MODULE_NOOP;
 
-       if (!pairfind(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
-               RDEBUG2("Request didn't contain an EAP-Message, not inserting EAP-Failure");
+       if (!fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
+               RDEBUG3("Request didn't contain an EAP-Message, not inserting EAP-Failure");
                return RLM_MODULE_NOOP;
        }
 
-       if (pairfind(request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
-               RDEBUG2("Reply already contained an EAP-Message, not inserting EAP-Failure");
+       if (fr_pair_find_by_num(request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
+               RDEBUG3("Reply already contained an EAP-Message, not inserting EAP-Failure");
                return RLM_MODULE_NOOP;
        }
 
@@ -754,9 +722,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         * Make sure there's a message authenticator attribute in the response
         * RADIUS protocol code will calculate the correct value later...
         */
-       vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
        if (!vp) {
-               pairmake_reply("Message-Authenticator", "0x00", T_OP_EQ);
+               pair_make_reply("Message-Authenticator", "0x00", T_OP_EQ);
        }
 
        return RLM_MODULE_UPDATED;
@@ -768,25 +736,18 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
  */
 extern module_t rlm_eap;
 module_t rlm_eap = {
-       RLM_MODULE_INIT,
-       "eap",
-       0,      /* type */
-       sizeof(rlm_eap_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "eap",
+       .inst_size      = sizeof(rlm_eap_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
 #ifdef WITH_PROXY
-               mod_post_proxy,         /* post-proxy */
-#else
-               NULL,
+               [MOD_POST_PROXY]        = mod_post_proxy,
 #endif
-               mod_post_auth           /* post-auth */
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
index 409f104..0edf462 100644 (file)
@@ -50,7 +50,6 @@ typedef struct eap_module {
 typedef struct rlm_eap {
        rbtree_t        *session_tree;
        eap_handler_t   *session_head, *session_tail;
-       rbtree_t        *handler_tree; /* for debugging only */
        eap_module_t    *methods[PW_EAP_MAX_TYPES];
 
        /*
@@ -90,7 +89,7 @@ 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_instantiate(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 */
index 9ceb76a..454e1e7 100644 (file)
@@ -42,10 +42,8 @@ typedef struct rlm_eap_gtc_t {
 
 static CONF_PARSER module_config[] = {
        { "challenge", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_gtc_t, challenge), "Password: " },
-
        { "auth_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_gtc_t, auth_type_name), "PAP" },
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -53,7 +51,7 @@ static CONF_PARSER module_config[] = {
 /*
  *     Attach the module.
  */
-static int gtc_attach(CONF_SECTION *cs, void **instance)
+static int mod_instantiate(CONF_SECTION *cs, void **instance)
 {
        rlm_eap_gtc_t   *inst;
        DICT_VALUE      *dval;
@@ -78,7 +76,7 @@ static int gtc_attach(CONF_SECTION *cs, void **instance)
 
                inst->auth_type = dval->value;
        } else {
-               inst->auth_type = PW_AUTHTYPE_LOCAL;
+               inst->auth_type = PW_AUTH_TYPE_LOCAL;
        }
        return 0;
 }
@@ -86,7 +84,7 @@ static int gtc_attach(CONF_SECTION *cs, void **instance)
 /*
  *     Initiate the EAP-GTC session by sending a challenge to the peer.
  */
-static int gtc_initiate(void *instance, eap_handler_t *handler)
+static int mod_session_init(void *instance, eap_handler_t *handler)
 {
        char challenge_str[1024];
        int length;
@@ -120,7 +118,7 @@ static int gtc_initiate(void *instance, eap_handler_t *handler)
         *      stored in 'handler->eap_ds', which will be given back
         *      to us...
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
@@ -129,7 +127,7 @@ static int gtc_initiate(void *instance, eap_handler_t *handler)
 /*
  *     Authenticate a previously sent challenge.
  */
-static int CC_HINT(nonnull) mod_authenticate(void *instance, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_process(void *instance, eap_handler_t *handler)
 {
        VALUE_PAIR *vp;
        EAP_DS *eap_ds = handler->eap_ds;
@@ -139,7 +137,7 @@ static int CC_HINT(nonnull) mod_authenticate(void *instance, eap_handler_t *hand
        /*
         *      Get the Cleartext-Password for this user.
         */
-       rad_assert(handler->stage == AUTHENTICATE);
+       rad_assert(handler->stage == PROCESS);
 
        /*
         *      Sanity check the response.  We need at least one byte
@@ -152,7 +150,7 @@ static int CC_HINT(nonnull) mod_authenticate(void *instance, eap_handler_t *hand
        }
 
 #if 0
-       if ((debug_flag > 2) && fr_log_fp) {
+       if ((rad_debug_lvl > 2) && fr_log_fp) {
                int i;
 
                for (i = 0; i < eap_ds->response->length - 4; i++) {
@@ -168,11 +166,11 @@ static int CC_HINT(nonnull) mod_authenticate(void *instance, eap_handler_t *hand
        /*
         *      Handle passwords here.
         */
-       if (inst->auth_type == PW_AUTHTYPE_LOCAL) {
+       if (inst->auth_type == PW_AUTH_TYPE_LOCAL) {
                /*
                 *      For now, do cleartext password authentication.
                 */
-               vp = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
                if (!vp) {
                        REDEBUG2("Cleartext-Password is required for authentication");
                        eap_ds->request->code = PW_EAP_FAILURE;
@@ -204,9 +202,9 @@ static int CC_HINT(nonnull) mod_authenticate(void *instance, eap_handler_t *hand
                 *      If there was a User-Password in the request,
                 *      why the heck are they using EAP-GTC?
                 */
-               pairdelete(&request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+               fr_pair_delete_by_num(&request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
 
-               vp = pairmake_packet("User-Password", NULL, T_OP_EQ);
+               vp = pair_make_request("User-Password", NULL, T_OP_EQ);
                if (!vp) {
                        return 0;
                }
@@ -249,10 +247,8 @@ static int CC_HINT(nonnull) mod_authenticate(void *instance, eap_handler_t *hand
  */
 extern rlm_eap_module_t rlm_eap_gtc;
 rlm_eap_module_t rlm_eap_gtc = {
-       "eap_gtc",
-       gtc_attach,                     /* attach */
-       gtc_initiate,                   /* Start the initial request */
-       NULL,                           /* authorization */
-       mod_authenticate,               /* authentication */
-       NULL                            /* detach */
+       .name           = "eap_gtc",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };
index 36b9ec2..dda4f67 100644 (file)
@@ -82,7 +82,7 @@ int getusersfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list, c
         *      Walk through the 'users' file list, if we're debugging,
         *      or if we're in compat_mode.
         */
-       if ((debug_flag) ||
+       if ((rad_debug_lvl) ||
                (strcmp(compat_mode_str, "cistron") == 0)) {
                PAIR_LIST *entry;
                VALUE_PAIR *vp;
@@ -311,7 +311,7 @@ void rad_update_shared_seclist(struct sharedSecList **list, char const *id, VALU
        }
 
        //idtype
-       vp = pairfind(items, RAD_EAP_IKEV2_IDTYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(items, RAD_EAP_IKEV2_IDTYPE, 0, TAG_ANY);
        if (!vp) {
                DEBUG(IKEv2_LOG_PREFIX "[%s] -- Id type not set", id);
        } else {
@@ -322,7 +322,7 @@ void rad_update_shared_seclist(struct sharedSecList **list, char const *id, VALU
        }
 
        //secret
-       vp = pairfind(items, RAD_EAP_IKEV2_SECRET, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(items, RAD_EAP_IKEV2_SECRET, 0, TAG_ANY);
        if (!vp || !vp->vp_length) {
                DEBUG(IKEv2_LOG_PREFIX "[%s] -- Secret not set", id);
        } else {
@@ -330,7 +330,7 @@ void rad_update_shared_seclist(struct sharedSecList **list, char const *id, VALU
        }
 
        //authtype
-       vp = pairfind(items, RAD_EAP_IKEV2_AUTHTYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(items, RAD_EAP_IKEV2_AUTHTYPE, 0, TAG_ANY);
        if (vp && vp->vp_length) {
                authtype = AuthtypeFromName(vp->vp_strvalue);
 
index 02df628..9733632 100644 (file)
@@ -95,8 +95,7 @@ CONF_PARSER module_config[] = {
 
        { "fast_dh_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ikev2_t, enable_fast_dhex), "no" },
        { "enable_fast_reauth", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ikev2_t, enable_fast_reconnect), "yes" },
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 static int set_mppe_keys(eap_handler_t *handler)
@@ -148,7 +147,7 @@ static int compose_rad_message(uint8_t *out,u_int32_t olen, EAP_DS *eap_ds) {
 /** Free memory after EAP-IKEv2 module usage
  *
  */
-static int ikev2_detach(void *instance)
+static int mod_detach(void *instance)
 {
        struct ikev2_ctx *data = (struct ikev2_ctx *) instance;
 
@@ -192,7 +191,7 @@ static void ikev2_free_opaque(void *opaque)
 /** Configure EAP-ikev2 handler
  *
  */
-static int ikev2_attach(CONF_SECTION *conf, void **instance)
+static int mod_instantiate(CONF_SECTION *conf, void **instance)
 {
        int ret;
 
@@ -305,7 +304,7 @@ static int ikev2_attach(CONF_SECTION *conf, void **instance)
 /** Initiate the EAP-ikev2 session by sending a challenge to the peer.
  *
  */
-static int ikev2_initiate(void *instance, eap_handler_t *handler)
+static int mod_session_init(void *instance, eap_handler_t *handler)
 {
        INFO(IKEv2_LOG_PREFIX "Initiate connection!");
 
@@ -367,14 +366,14 @@ static int ikev2_initiate(void *instance, eap_handler_t *handler)
         *      stored in 'handler->eap_ds', which will be given back
         *      to us...
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
        return 1;
 }
 
 /** Authenticate a previously sent challenge
  *
  */
-static int ikev2_authenticate(void *instance, eap_handler_t *handler)
+static int mod_process(void *instance, eap_handler_t *handler)
 {
        uint8_t *in;
        uint8_t *out = NULL;
@@ -395,7 +394,7 @@ static int ikev2_authenticate(void *instance, eap_handler_t *handler)
        INFO(IKEv2_LOG_PREFIX "authenticate" );
 
        rad_assert(handler->request != NULL);
-       rad_assert(handler->stage == AUTHENTICATE);
+       rad_assert(handler->stage == PROCESS);
 
        EAP_DS *eap_ds=handler->eap_ds;
        if (!eap_ds ||
@@ -519,10 +518,9 @@ static int ikev2_authenticate(void *instance, eap_handler_t *handler)
  */
 extern rlm_eap_module_t rlm_eap_ikev2;
 rlm_eap_module_t rlm_eap_ikev2 = {
-       "eap_ikev2",
-       ikev2_attach,                   /* attach */
-       ikev2_initiate,                 /* Start the initial request */
-       NULL,                           /* authorization */
-       ikev2_authenticate,             /* authentication */
-       ikev2_detach                    /* detach */
+       .name           = "eap_ikev2",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process,          /* Process next round of EAP method */
+       .detach         = mod_detach            /* detach */
 };
index cb60527..6cce0f8 100644 (file)
@@ -50,6 +50,9 @@ RCSID("$Id$")
 #include <stdlib.h>
 #include "eap.h"
 #include "eap_leap.h"
+
+#include <freeradius-devel/md5.h>
+
 /*
  *   Extract the data from the LEAP packet.
  */
@@ -188,7 +191,7 @@ static int eapleap_ntpwdhash(uint8_t *out, REQUEST *request, VALUE_PAIR *passwor
                }
 
                if (p) {
-                       pairmemcpy(password, p, 16);
+                       fr_pair_value_memcpy(password, p, 16);
                        talloc_free(p);
                }
 
@@ -297,7 +300,7 @@ leap_packet_t *eapleap_stage6(REQUEST *request, leap_packet_t *packet, VALUE_PAI
        /*
         *      Calculate the leap:session-key attribute
         */
-       vp = pairmake_reply("Cisco-AVPair", NULL, T_OP_ADD);
+       vp = pair_make_reply("Cisco-AVPair", NULL, T_OP_ADD);
        if (!vp) {
                REDEBUG("Failed to create Cisco-AVPair attribute.  LEAP cancelled");
                talloc_free(reply);
@@ -330,7 +333,7 @@ leap_packet_t *eapleap_stage6(REQUEST *request, leap_packet_t *packet, VALUE_PAI
 
        i = 16;
        rad_tunnel_pwencode(q + 17, &i, request->client->secret, request->packet->vector);
-       pairstrsteal(vp, q);
+       fr_pair_value_strsteal(vp, q);
        vp->vp_length = 17 + i;
 
        return reply;
index 2a78567..57ecaed 100644 (file)
@@ -18,7 +18,7 @@ RCSIDH(eap_leap_h, "$Id$")
 
 /*
  ****
- * EAP - LEAP doesnot specify code, id & length but chap specifies them,
+ * EAP - LEAP does not specify code, id & length but chap specifies them,
  *     for generalization purpose, complete header should be sent
  *     and not just value_size, value and name.
  *     future implementation.
index 1502907..c3028d5 100644 (file)
@@ -37,7 +37,7 @@ RCSID("$Id$")
  * len = header + type + leap_methoddata
  * leap_methoddata = value_size + value
  */
-static int CC_HINT(nonnull) leap_initiate(UNUSED void *instance, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_session_init(UNUSED void *instance, eap_handler_t *handler)
 {
        REQUEST         *request = handler->request;
        leap_session_t  *session;
@@ -81,13 +81,13 @@ static int CC_HINT(nonnull) leap_initiate(UNUSED void *instance, eap_handler_t *
        /*
         *      The next stage to process the packet.
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        talloc_free(reply);
        return 1;
 }
 
-static int CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_process(UNUSED void *instance, eap_handler_t *handler)
 {
        int             rcode;
        REQUEST         *request = handler->request;
@@ -114,9 +114,9 @@ static int CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, eap_handler_
         *      The password is never sent over the wire.
         *      Always get the configured password, for each user.
         */
-       password = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+       password = fr_pair_find_by_num(handler->request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
        if (!password) {
-               password = pairfind(handler->request->config_items, PW_NT_PASSWORD, 0, TAG_ANY);
+               password = fr_pair_find_by_num(handler->request->config, PW_NT_PASSWORD, 0, TAG_ANY);
        }
 
        if (!password) {
@@ -198,10 +198,7 @@ static int CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, eap_handler_
  */
 extern rlm_eap_module_t rlm_eap_leap;
 rlm_eap_module_t rlm_eap_leap = {
-       "eap_leap",
-       NULL,                   /* attach */
-       leap_initiate,          /* Start the initial request, after Identity */
-       NULL,                   /* authorization */
-       mod_authenticate,       /* authentication */
-       NULL,                   /* detach */
+       .name           = "eap_leap",
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };
index 9887b56..56baac0 100644 (file)
@@ -42,6 +42,7 @@ RCSID("$Id$")
 #include "eap.h"
 
 #include "eap_md5.h"
+#include <freeradius-devel/md5.h>
 
 /*
  *     We expect only RESPONSE for which SUCCESS or FAILURE is sent back
index 5fc4697..aafa407 100644 (file)
@@ -16,7 +16,7 @@ RCSIDH(eap_md5_h, "$Id$")
 
 /*
  ****
- * EAP - MD5 doesnot specify code, id & length but chap specifies them,
+ * EAP - MD5 does not specify code, id & length but chap specifies them,
  *     for generalization purpose, complete header should be sent
  *     and not just value_size, value and name.
  *     future implementation.
index 59b3cae..7277462 100644 (file)
@@ -29,11 +29,12 @@ RCSID("$Id$")
 #include "eap_md5.h"
 
 #include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/md5.h>
 
 /*
  *     Initiate the EAP-MD5 session by sending a challenge to the peer.
  */
-static int md5_initiate(UNUSED void *instance, eap_handler_t *handler)
+static int mod_session_init(UNUSED void *instance, eap_handler_t *handler)
 {
        int             i;
        MD5_PACKET      *reply;
@@ -92,16 +93,15 @@ static int md5_initiate(UNUSED void *instance, eap_handler_t *handler)
         *      stored in 'handler->eap_ds', which will be given back
         *      to us...
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
 
-
 /*
  *     Authenticate a previously sent challenge.
  */
-static int md5_authenticate(UNUSED void *arg, eap_handler_t *handler)
+static int mod_process(UNUSED void *arg, eap_handler_t *handler)
 {
        MD5_PACKET      *packet;
        MD5_PACKET      *reply;
@@ -112,9 +112,9 @@ static int md5_authenticate(UNUSED void *arg, eap_handler_t *handler)
         *      Get the Cleartext-Password for this user.
         */
        rad_assert(handler->request != NULL);
-       rad_assert(handler->stage == AUTHENTICATE);
+       rad_assert(handler->stage == PROCESS);
 
-       password = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+       password = fr_pair_find_by_num(handler->request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
        if (!password) {
                RDEBUG2("Cleartext-Password is required for EAP-MD5 authentication");
                return 0;
@@ -162,10 +162,7 @@ static int md5_authenticate(UNUSED void *arg, eap_handler_t *handler)
  */
 extern rlm_eap_module_t rlm_eap_md5;
 rlm_eap_module_t rlm_eap_md5 = {
-       "eap_md5",
-       NULL,                           /* attach */
-       md5_initiate,                   /* Start the initial request */
-       NULL,                           /* authorization */
-       md5_authenticate,               /* authentication */
-       NULL                            /* detach */
+       .name           = "eap_md5",
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };
index e582092..8cf9094 100644 (file)
@@ -32,29 +32,30 @@ RCSID("$Id$")
 typedef struct rlm_eap_mschapv2_t {
        bool with_ntdomain_hack;
        bool send_error;
+       char const *identity;
 } rlm_eap_mschapv2_t;
 
 static CONF_PARSER module_config[] = {
        { "with_ntdomain_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_mschapv2_t, with_ntdomain_hack), "no" },
 
        { "send_error", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_mschapv2_t, send_error), "no" },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_mschapv2_t, identity), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 
 static void fix_mppe_keys(eap_handler_t *handler, mschapv2_opaque_t *data)
 {
-       pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
-       pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
-       pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
-       pairfilter(data, &data->mppe_keys, &handler->request->reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
+       fr_pair_list_mcopy_by_num(data, &data->mppe_keys, &handler->request->reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
+       fr_pair_list_mcopy_by_num(data, &data->mppe_keys, &handler->request->reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
+       fr_pair_list_mcopy_by_num(data, &data->mppe_keys, &handler->request->reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
+       fr_pair_list_mcopy_by_num(data, &data->mppe_keys, &handler->request->reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
 }
 
 /*
  *     Attach the module.
  */
-static int mschapv2_attach(CONF_SECTION *cs, void **instance)
+static int mod_instantiate(CONF_SECTION *cs, void **instance)
 {
        rlm_eap_mschapv2_t *inst;
 
@@ -68,6 +69,15 @@ static int mschapv2_attach(CONF_SECTION *cs, void **instance)
                return -1;
        }
 
+       if (inst->identity && (strlen(inst->identity) > 255)) {
+               cf_log_err_cs(cs, "identity is too long");
+               return -1;
+       }
+
+       if (!inst->identity) {
+               inst->identity = talloc_asprintf(inst, "freeradius-%s", RADIUSD_VERSION_STRING);
+       }
+
        return 0;
 }
 
@@ -75,7 +85,7 @@ static int mschapv2_attach(CONF_SECTION *cs, void **instance)
 /*
  *     Compose the response.
  */
-static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
+static int eapmschapv2_compose(rlm_eap_mschapv2_t *inst, eap_handler_t *handler, VALUE_PAIR *reply)
 {
        uint8_t *ptr;
        int16_t length;
@@ -103,11 +113,12 @@ static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 *  |                             Challenge...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-                *  |                             Name...
+                *  |                             Server Name...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 */
-               length = MSCHAPV2_HEADER_LEN + MSCHAPV2_CHALLENGE_LEN + strlen(handler->identity);
+               length = MSCHAPV2_HEADER_LEN + MSCHAPV2_CHALLENGE_LEN + strlen(inst->identity);
                eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, length);
+
                /*
                 *      Allocate room for the EAP-MS-CHAPv2 data.
                 */
@@ -131,7 +142,8 @@ static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
                 *      Copy the Challenge, success, or error over.
                 */
                memcpy(ptr, reply->vp_octets, reply->vp_length);
-               memcpy((ptr + reply->vp_length), handler->identity, strlen(handler->identity));
+
+               memcpy((ptr + reply->vp_length), inst->identity, strlen(inst->identity));
                break;
 
        case PW_MSCHAP2_SUCCESS:
@@ -200,7 +212,7 @@ static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
 /*
  *     Initiate the EAP-MSCHAPV2 session by sending a challenge to the peer.
  */
-static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
+static int mod_session_init(void *instance, eap_handler_t *handler)
 {
        int             i;
        VALUE_PAIR      *challenge;
@@ -208,8 +220,9 @@ static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
        REQUEST         *request = handler->request;
        uint8_t         *p;
        bool            created_challenge = false;
+       rlm_eap_mschapv2_t *inst = instance;
 
-       challenge = pairfind(request->config_items, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
+       challenge = fr_pair_find_by_num(request->config, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
        if (challenge && (challenge->vp_length != MSCHAPV2_CHALLENGE_LEN)) {
                RWDEBUG("control:MS-CHAP-Challenge is incorrect length.  Ignoring it.");
                challenge = NULL;
@@ -217,7 +230,7 @@ static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
 
        if (!challenge) {
                created_challenge = true;
-               challenge = pairmake(handler, NULL, "MS-CHAP-Challenge", NULL, T_OP_EQ);
+               challenge = fr_pair_make(handler, NULL, "MS-CHAP-Challenge", NULL, T_OP_EQ);
 
                /*
                 *      Get a random challenge.
@@ -250,8 +263,8 @@ static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
         *      Compose the EAP-MSCHAPV2 packet out of the data structure,
         *      and free it.
         */
-       eapmschapv2_compose(handler, challenge);
-       if (created_challenge) pairfree(&challenge);
+       eapmschapv2_compose(inst, handler, challenge);
+       if (created_challenge) fr_pair_list_free(&challenge);
 
 #ifdef WITH_PROXY
        /*
@@ -268,7 +281,7 @@ static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
         *      stored in 'handler->eap_ds', which will be given back
         *      to us...
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
@@ -303,7 +316,7 @@ static int CC_HINT(nonnull) mschap_postproxy(eap_handler_t *handler, UNUSED void
                 *      Move the attribute, so it doesn't go into
                 *      the reply.
                 */
-               pairfilter(data, &response, &request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_list_mcopy_by_num(data, &response, &request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
                break;
 
        default:
@@ -324,7 +337,7 @@ static int CC_HINT(nonnull) mschap_postproxy(eap_handler_t *handler, UNUSED void
         *      Done doing EAP proxy stuff.
         */
        request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
-       eapmschapv2_compose(handler, response);
+       eapmschapv2_compose(NULL, handler, response);
        data->code = PW_EAP_MSCHAPV2_SUCCESS;
 
        /*
@@ -339,14 +352,14 @@ static int CC_HINT(nonnull) mschap_postproxy(eap_handler_t *handler, UNUSED void
         *      access-accept e.g. vlan, etc. This lets the PEAP
         *      use_tunneled_reply code work
         */
-       data->reply = paircopy(data, request->reply->vps);
+       data->reply = fr_pair_list_copy(data, request->reply->vps);
 
        /*
         *      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_CODE_ACCESS_CHALLENGE;
-       pairfree(&response);
+       fr_pair_list_free(&response);
 
        return 1;
 }
@@ -355,7 +368,7 @@ static int CC_HINT(nonnull) mschap_postproxy(eap_handler_t *handler, UNUSED void
 /*
  *     Authenticate a previously sent challenge.
  */
-static int CC_HINT(nonnull) mschapv2_authenticate(void *arg, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_process(void *arg, eap_handler_t *handler)
 {
        int rcode, ccode;
        uint8_t *p;
@@ -367,7 +380,7 @@ static int CC_HINT(nonnull) mschapv2_authenticate(void *arg, eap_handler_t *hand
        rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
        REQUEST *request = handler->request;
 
-       rad_assert(handler->stage == AUTHENTICATE);
+       rad_assert(handler->stage == PROCESS);
 
        data = (mschapv2_opaque_t *) handler->opaque;
 
@@ -401,11 +414,11 @@ static int CC_HINT(nonnull) mschapv2_authenticate(void *arg, eap_handler_t *hand
 
                        RDEBUG2("Password change packet received");
 
-                       challenge = pairmake_packet("MS-CHAP-Challenge", NULL, T_OP_EQ);
+                       challenge = pair_make_request("MS-CHAP-Challenge", NULL, T_OP_EQ);
                        if (!challenge) return 0;
-                       pairmemcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);
+                       fr_pair_value_memcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);
 
-                       cpw = pairmake_packet("MS-CHAP2-CPW", NULL, T_OP_EQ);
+                       cpw = pair_make_request("MS-CHAP2-CPW", NULL, T_OP_EQ);
                        cpw->vp_length = 68;
 
                        cpw->vp_octets = p = talloc_array(cpw, uint8_t, cpw->vp_length);
@@ -422,7 +435,7 @@ static int CC_HINT(nonnull) mschapv2_authenticate(void *arg, eap_handler_t *hand
                                int to_copy = 516 - copied;
                                if (to_copy > 243) to_copy = 243;
 
-                               nt_enc = pairmake_packet("MS-CHAP-NT-Enc-PW", NULL, T_OP_ADD);
+                               nt_enc = pair_make_request("MS-CHAP-NT-Enc-PW", NULL, T_OP_ADD);
                                nt_enc->vp_length = 4 + to_copy;
 
                                nt_enc->vp_octets = p = talloc_array(nt_enc, uint8_t, nt_enc->vp_length);
@@ -469,7 +482,7 @@ failure:
                case PW_EAP_MSCHAPV2_SUCCESS:
                        eap_ds->request->code = PW_EAP_SUCCESS;
 
-                       pairfilter(request->reply, &request->reply->vps, &data->mppe_keys, 0, 0, TAG_ANY);
+                       fr_pair_list_mcopy_by_num(request->reply, &request->reply->vps, &data->mppe_keys, 0, 0, TAG_ANY);
                        /* FALL-THROUGH */
 
                case PW_EAP_MSCHAPV2_ACK:
@@ -479,7 +492,7 @@ failure:
                         */
                        request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
 #endif
-                       pairfilter(request->reply, &request->reply->vps, &data->reply, 0, 0, TAG_ANY);
+                       fr_pair_list_mcopy_by_num(request->reply, &request->reply->vps, &data->reply, 0, 0, TAG_ANY);
                        return 1;
                }
                REDEBUG("Sent SUCCESS expecting SUCCESS (or ACK) but got %d", ccode);
@@ -547,11 +560,11 @@ failure:
         *      to pass to the 'mschap' module.  This is a little wonky,
         *      but it works.
         */
-       challenge = pairmake_packet("MS-CHAP-Challenge", NULL, T_OP_EQ);
+       challenge = pair_make_request("MS-CHAP-Challenge", NULL, T_OP_EQ);
        if (!challenge) return 0;
-       pairmemcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);
+       fr_pair_value_memcpy(challenge, data->challenge, MSCHAPV2_CHALLENGE_LEN);
 
-       response = pairmake_packet("MS-CHAP2-Response", NULL, T_OP_EQ);
+       response = pair_make_request("MS-CHAP2-Response", NULL, T_OP_EQ);
        if (!response) return 0;
        response->vp_length = MSCHAPV2_RESPONSE_LEN;
        response->vp_octets = p = talloc_array(response, uint8_t, response->vp_length);
@@ -560,7 +573,7 @@ failure:
        p[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN];
        memcpy(p + 2, &eap_ds->response->type.data[5], MSCHAPV2_RESPONSE_LEN - 2);
 
-       name = pairmake_packet("MS-CHAP-User-Name", NULL, T_OP_EQ);
+       name = pair_make_request("MS-CHAP-User-Name", NULL, T_OP_EQ);
        if (!name) return 0;
 
        /*
@@ -617,7 +630,7 @@ packet_ready:
                 *      the State attribute back, before passing
                 *      the handler & request back into the tunnel.
                 */
-               pairdelete(&request->packet->vps, PW_STATE, 0, TAG_ANY);
+               fr_pair_delete_by_num(&request->packet->vps, PW_STATE, 0, TAG_ANY);
 
                /*
                 *      Fix the User-Name when proxying, to strip off
@@ -626,7 +639,7 @@ packet_ready:
                 *      in the user name, THEN discard the user name.
                 */
                if (inst->with_ntdomain_hack &&
-                   ((challenge = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) &&
+                   ((challenge = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY)) != NULL) &&
                    ((username = strchr(challenge->vp_strvalue, '\\')) != NULL)) {
                        /*
                         *      Wipe out the NT domain.
@@ -634,7 +647,7 @@ packet_ready:
                         *      FIXME: Put it into MS-CHAP-Domain?
                         */
                        username++; /* skip the \\ */
-                       pairstrcpy(challenge, username);
+                       fr_pair_value_strcpy(challenge, username);
                }
 
                /*
@@ -649,7 +662,7 @@ packet_ready:
        /*
         *      This is a wild & crazy hack.
         */
-       rcode = process_authenticate(PW_AUTHTYPE_MS_CHAP, request);
+       rcode = process_authenticate(PW_AUTH_TYPE_MS_CHAP, request);
 
        /*
         *      Delete MPPE keys & encryption policy.  We don't
@@ -663,10 +676,10 @@ packet_ready:
         */
        response = NULL;
        if (rcode == RLM_MODULE_OK) {
-               pairfilter(data, &response, &request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_list_mcopy_by_num(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);
+               fr_pair_list_mcopy_by_num(data, &response, &request->reply->vps, PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY);
                if (response) {
                        int n,err,retry;
                        char buf[34];
@@ -676,7 +689,7 @@ packet_ready:
                        RDEBUG2("MSCHAP-Error: %s", response->vp_strvalue);
 
                        /*
-                        *      Pxarse the new challenge out of the
+                        *      Parse the new challenge out of the
                         *      MS-CHAP-Error, so that if the client
                         *      issues a re-try, we will know which
                         *      challenge value that they used.
@@ -708,8 +721,8 @@ packet_ready:
         *      Compose the response (whatever it is),
         *      and return it to the over-lying EAP module.
         */
-       eapmschapv2_compose(handler, response);
-       pairfree(&response);
+       eapmschapv2_compose(inst, handler, response);
+       fr_pair_list_free(&response);
 
        return 1;
 }
@@ -720,10 +733,8 @@ packet_ready:
  */
 extern rlm_eap_module_t rlm_eap_mschapv2;
 rlm_eap_module_t rlm_eap_mschapv2 = {
-       "eap_mschapv2",
-       mschapv2_attach,                /* attach */
-       mschapv2_initiate,              /* Start the initial request */
-       NULL,                           /* authorization */
-       mschapv2_authenticate,          /* authentication */
-       NULL                            /* detach */
+       .name           = "eap_mschapv2",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };
index 57a4012..3f575e4 100644 (file)
@@ -165,7 +165,7 @@ static void eapsoh_verify(REQUEST *request, RADIUS_PACKET *packet,
        uint32_t eap_method;
        int rv;
 
-       vp = pairmake(packet, &packet->vps, "SoH-Supported", "no", T_OP_EQ);
+       vp = fr_pair_make(packet, &packet->vps, "SoH-Supported", "no", T_OP_EQ);
        if (data && data[0] == PW_EAP_NAK) {
                RDEBUG("SoH - client NAKed");
                return;
@@ -251,7 +251,7 @@ static int eapmessage_verify(REQUEST *request,
                 */
        case PW_EAP_MSCHAPV2:
        default:
-               RDEBUG2("EAP type %s (%d)", eap_type2name(eap_method),
+               RDEBUG2("EAP method %s (%d)", eap_type2name(eap_method),
                        eap_method);
                return 1;
        }
@@ -273,7 +273,7 @@ static VALUE_PAIR *eap2vp(UNUSED REQUEST *request, RADIUS_PACKET *packet,
 
        if (data_len > 65535) return NULL; /* paranoia */
 
-       vp = paircreate(packet, PW_EAP_MESSAGE, 0);
+       vp = fr_pair_afrom_num(packet, PW_EAP_MESSAGE, 0);
        if (!vp) {
                return NULL;
        }
@@ -297,13 +297,13 @@ static VALUE_PAIR *eap2vp(UNUSED REQUEST *request, RADIUS_PACKET *packet,
        fr_cursor_init(&cursor, &head);
        fr_cursor_insert(&cursor, vp);
        while (total < data_len) {
-               vp = paircreate(packet, PW_EAP_MESSAGE, 0);
+               vp = fr_pair_afrom_num(packet, PW_EAP_MESSAGE, 0);
                if (!vp) {
-                       pairfree(&head);
+                       fr_pair_list_free(&head);
                        return NULL;
                }
 
-               pairmemcpy(vp, data + total, (data_len - total));
+               fr_pair_value_memcpy(vp, data + total, (data_len - total));
 
                total += vp->vp_length;
 
@@ -329,7 +329,7 @@ static int vp2eap(REQUEST *request, tls_session_t *tls_session, VALUE_PAIR *vp)
         *      type & data to the client.
         */
 #ifndef NDEBUG
-       if ((debug_flag > 2) && fr_log_fp) {
+       if ((rad_debug_lvl > 2) && fr_log_fp) {
                size_t i, total, start = EAP_HEADER_LEN;
                total = 0;
 
@@ -419,7 +419,7 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
        VALUE_PAIR *vp;
        peap_tunnel_t *t = tls_session->opaque;
 
-       if ((debug_flag > 0) && fr_log_fp) {
+       if ((rad_debug_lvl > 0) && fr_log_fp) {
                RDEBUG("Got tunneled reply RADIUS code %d", reply->code);
                rdebug_pair_list(L_DBG_LVL_1, request, reply->vps, NULL);
        }
@@ -444,21 +444,21 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                        /*
                         *      Clean up the tunneled reply.
                         */
-                       pairdelete(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
-                       pairdelete(&reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
-                       pairdelete(&reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
 
                        /*
                         *      Delete MPPE keys & encryption policy.  We don't
                         *      want these here.
                         */
-                       pairdelete(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
-                       pairdelete(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
-                       pairdelete(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
-                       pairdelete(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
 
-                       pairfree(&t->accept_vps); /* for proxying MS-CHAP2 */
-                       pairfilter(t, &t->accept_vps, &reply->vps, 0, 0, TAG_ANY);
+                       fr_pair_list_free(&t->accept_vps); /* for proxying MS-CHAP2 */
+                       fr_pair_list_mcopy_by_num(t, &t->accept_vps, &reply->vps, 0, 0, TAG_ANY);
                        rad_assert(!reply->vps);
                }
                break;
@@ -478,8 +478,8 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 *
                 *      Get rid of the old State, too.
                 */
-               pairfree(&t->state);
-               pairfilter(t, &t->state, &reply->vps, PW_STATE, 0, TAG_ANY);
+               fr_pair_list_free(&t->state);
+               fr_pair_list_mcopy_by_num(t, &t->state, &reply->vps, PW_STATE, 0, TAG_ANY);
 
                /*
                 *      PEAP takes only EAP-Message attributes inside
@@ -487,7 +487,7 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 *      Access-Challenge is ignored.
                 */
                vp = NULL;
-               pairfilter(t, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+               fr_pair_list_mcopy_by_num(t, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
 
                /*
                 *      Handle EAP-MSCHAP-V2, where Access-Accept's
@@ -502,11 +502,11 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                        /*
                         *      Clean up the tunneled reply.
                         */
-                       pairdelete(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
-                       pairdelete(&reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+                       fr_pair_delete_by_num(&reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
 
                        rad_assert(!t->accept_vps);
-                       pairfilter(t, &t->accept_vps, &reply->vps, 0, 0, TAG_ANY);
+                       fr_pair_list_mcopy_by_num(t, &t->accept_vps, &reply->vps, 0, 0, TAG_ANY);
                        rad_assert(!reply->vps);
                }
 
@@ -516,7 +516,7 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 */
                if (vp) {
                        vp2eap(request, tls_session, vp);
-                       pairfree(&vp);
+                       fr_pair_list_free(&vp);
                }
 
                rcode = RLM_MODULE_HANDLED;
@@ -571,7 +571,7 @@ static int CC_HINT(nonnull) eappeap_postproxy(eap_handler_t *handler, void *data
                fake->reply = talloc_steal(fake, request->proxy_reply);
                request->proxy_reply = NULL;
 
-               if ((debug_flag > 0) && fr_log_fp) {
+               if ((rad_debug_lvl > 0) && fr_log_fp) {
                        fprintf(fr_log_fp, "server %s {\n", fake->server);
                }
 
@@ -589,7 +589,7 @@ static int CC_HINT(nonnull) eappeap_postproxy(eap_handler_t *handler, void *data
                 */
                rcode = rad_postauth(fake);
 
-               if ((debug_flag > 0) && fr_log_fp) {
+               if ((rad_debug_lvl > 0) && fr_log_fp) {
                        fprintf(fr_log_fp, "} # server %s\n", fake->server);
 
                        RDEBUG("Final reply from tunneled session code %d", fake->reply->code);
@@ -639,7 +639,7 @@ static int CC_HINT(nonnull) eappeap_postproxy(eap_handler_t *handler, void *data
         *      the basis for the reply to the NAS.  We don't want that,
         *      so we toss it, after we've had our way with it.
         */
-       pairfree(&handler->request->proxy_reply->vps);
+       fr_pair_list_free(&handler->request->proxy_reply->vps);
 
        switch (rcode) {
        case RLM_MODULE_REJECT:
@@ -706,7 +706,7 @@ static void print_tunneled_data(uint8_t const *data, size_t data_len)
 {
        size_t i;
 
-       if ((debug_flag > 2) && fr_log_fp) {
+       if ((rad_debug_lvl > 2) && fr_log_fp) {
                for (i = 0; i < data_len; i++) {
                  if ((i & 0x0f) == 0) fprintf(fr_log_fp, "  PEAP tunnel data in %02x: ", (int) i);
 
@@ -730,7 +730,6 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
        rlm_rcode_t     rcode = RLM_MODULE_REJECT;
        uint8_t const   *data;
        unsigned int    data_len;
-       char *p;
 
        REQUEST *request = handler->request;
        EAP_DS *eap_ds = handler->eap_ds;
@@ -748,7 +747,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)) {
                REDEBUG("Tunneled data is invalid");
-               if (debug_flag > 2) print_tunneled_data(data, data_len);
+               if (rad_debug_lvl > 2) print_tunneled_data(data, data_len);
                return RLM_MODULE_REJECT;
        }
 
@@ -787,13 +786,11 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                /*
                 *      Save it for later.
                 */
-               t->username = pairmake(t, NULL, "User-Name", NULL, T_OP_EQ);
+               t->username = fr_pair_make(t, NULL, "User-Name", NULL, T_OP_EQ);
                rad_assert(t->username != NULL);
 
-               t->username->vp_strvalue = p = talloc_array(t->username, char, data_len);
-               memcpy(p, data + 1, data_len - 1);
-               t->username->vp_length = data_len - 1;
-               p[t->username->vp_length] = 0;
+               fr_pair_value_bstrncpy(t->username, data + 1, data_len - 1);
+
                RDEBUG("Got inner identity '%s'", t->username->vp_strvalue);
                if (t->soh) {
                        t->status = PEAP_STATUS_WAIT_FOR_SOH_RESPONSE;
@@ -826,7 +823,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
 
                /* save the SoH VPs */
                rad_assert(!t->soh_reply_vps);
-               pairfilter(t, &t->soh_reply_vps, &fake->reply->vps, 0, 0, TAG_ANY);
+               fr_pair_list_mcopy_by_num(t, &t->soh_reply_vps, &fake->reply->vps, 0, 0, TAG_ANY);
                rad_assert(!fake->reply->vps);
                talloc_free(fake);
 
@@ -917,7 +914,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
 
                t->status = PEAP_STATUS_PHASE2;
 
-               vp = paircreate(fake->packet, PW_EAP_MESSAGE, 0);
+               vp = fr_pair_afrom_num(fake->packet, PW_EAP_MESSAGE, 0);
                vp->vp_length = len;
                vp->vp_octets = q = talloc_array(vp, uint8_t, vp->vp_length);
 
@@ -930,11 +927,11 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                memcpy(q + EAP_HEADER_LEN + 1,
                       t->username->vp_strvalue, t->username->vp_length);
 
-               pairadd(&fake->packet->vps, vp);
+               fr_pair_add(&fake->packet->vps, vp);
 
                if (t->default_method != 0) {
                        RDEBUG2("Setting default EAP type for tunneled EAP session");
-                       vp = pairmake(fake, &fake->config_items, "EAP-Type", "0", T_OP_EQ);
+                       vp = fr_pair_make(fake, &fake->config, "EAP-Type", "0", T_OP_EQ);
                        vp->vp_integer = t->default_method;
                }
                break; }
@@ -967,13 +964,11 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                 *      EAP-Identity packet.
                 */
                if ((data[0] == PW_EAP_IDENTITY) && (data_len > 1)) {
-                       t->username = pairmake(t, NULL, "User-Name", NULL, T_OP_EQ);
+                       t->username = fr_pair_make(t, NULL, "User-Name", NULL, T_OP_EQ);
                        rad_assert(t->username != NULL);
 
-                       t->username->vp_strvalue = p = talloc_array(t->username, char, data_len);
-                       memcpy(p, data + 1, data_len - 1);
-                       t->username->vp_length = data_len - 1;
-                       p[t->username->vp_length] = 0;
+                       fr_pair_value_bstrncpy(t->username, data + 1, data_len - 1);
+
                        RDEBUG2("Got tunneled identity of %s", t->username->vp_strvalue);
 
                        /*
@@ -982,7 +977,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                         */
                        if (t->default_method != 0) {
                                RDEBUG2("Setting default EAP type for tunneled EAP session");
-                               vp = pairmake(fake, &fake->config_items, "EAP-Type", "0", T_OP_EQ);
+                               vp = fr_pair_make(fake, &fake->config, "EAP-Type", "0", T_OP_EQ);
                                vp->vp_integer = t->default_method;
                        }
                }
@@ -990,7 +985,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
 
        setup_fake_request(request, fake, t);
 
-       if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
                fake->server = vp->vp_strvalue;
 
        } else if (t->virtual_server) {
@@ -1024,7 +1019,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
        switch (fake->reply->code) {
        case 0:                 /* No reply code, must be proxied... */
 #ifdef WITH_PROXY
-               vp = pairfind(fake->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(fake->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
 
                if (vp) {
                        eap_tunnel_data_t *tunnel;
@@ -1057,7 +1052,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                                 *      Run the EAP authentication.
                                 */
                                RDEBUG2("Calling authenticate in order to initiate tunneled EAP session");
-                               rcode = process_authenticate(PW_AUTHTYPE_EAP, fake);
+                               rcode = process_authenticate(PW_AUTH_TYPE_EAP, fake);
                                if (rcode == RLM_MODULE_OK) {
                                        /*
                                         *      Authentication succeeded! Rah!
@@ -1087,7 +1082,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                                 *      EAP-Message into another set
                                 *      of attributes.
                                 */
-                               pairdelete(&fake->packet->vps,
+                               fr_pair_delete_by_num(&fake->packet->vps,
                                           PW_EAP_MESSAGE, 0, TAG_ANY);
                        }
 
@@ -1097,8 +1092,8 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      Tell the original request that it's going
                         *      to be proxied.
                         */
-                       pairfilter(request, &request->config_items,
-                                  &fake->config_items,
+                       fr_pair_list_mcopy_by_num(request, &request->config,
+                                  &fake->config,
                                   PW_PROXY_TO_REALM, 0, TAG_ANY);
 
                        /*
@@ -1196,11 +1191,11 @@ static int CC_HINT(nonnull) setup_fake_request(REQUEST *request, REQUEST *fake,
        /*
         *      Tell the request that it's a fake one.
         */
-       pairmake(fake->packet, &fake->packet->vps, "Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
+       fr_pair_make(fake->packet, &fake->packet->vps, "Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
 
        if (t->username) {
-               vp = paircopy(fake->packet, t->username);
-               pairadd(&fake->packet->vps, vp);
+               vp = fr_pair_list_copy(fake->packet, t->username);
+               fr_pair_add(&fake->packet->vps, vp);
                fake->username = vp;
                RDEBUG2("Setting User-Name to %s", fake->username->vp_strvalue);
        } else {
@@ -1212,8 +1207,8 @@ static int CC_HINT(nonnull) setup_fake_request(REQUEST *request, REQUEST *fake,
         *      Add the State attribute, too, if it exists.
         */
        if (t->state) {
-               vp = paircopy(fake->packet, t->state);
-               if (vp) pairadd(&fake->packet->vps, vp);
+               vp = fr_pair_list_copy(fake->packet, t->state);
+               if (vp) fr_pair_add(&fake->packet->vps, vp);
        }
 
        /*
@@ -1249,7 +1244,7 @@ static int CC_HINT(nonnull) setup_fake_request(REQUEST *request, REQUEST *fake,
                         *      AND attributes which are copied there
                         *      from below.
                         */
-                       if (pair_find_by_da(fake->packet->vps, vp->da, TAG_ANY)) continue;
+                       if (fr_pair_find_by_da(fake->packet->vps, vp->da, TAG_ANY)) continue;
 
                        /*
                         *      Some attributes are handled specially.
@@ -1281,8 +1276,8 @@ static int CC_HINT(nonnull) setup_fake_request(REQUEST *request, REQUEST *fake,
                         *      Don't copy from the head, we've already
                         *      checked it.
                         */
-                       copy = paircopy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
-                       pairadd(&fake->packet->vps, copy);
+                       copy = fr_pair_list_copy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
+                       fr_pair_add(&fake->packet->vps, copy);
                }
        }
 
index ad448f2..15ea812 100644 (file)
@@ -50,7 +50,7 @@ typedef struct rlm_eap_peap_t {
 static CONF_PARSER module_config[] = {
        { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, tls_conf_name), NULL },
 
-       { "default_method", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, default_method_name), "mschapv2" },
+       { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, default_method_name), "mschapv2" },
 
        { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, copy_request_to_tunnel), "no" },
 
@@ -67,15 +67,14 @@ static CONF_PARSER module_config[] = {
        { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, req_client_cert), "no" },
 
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 
 /*
  *     Attach the module.
  */
-static int eappeap_attach(CONF_SECTION *cs, void **instance)
+static int mod_instantiate(CONF_SECTION *cs, void **instance)
 {
        rlm_eap_peap_t          *inst;
 
@@ -140,7 +139,7 @@ static peap_tunnel_t *peap_alloc(TALLOC_CTX *ctx, rlm_eap_peap_t *inst)
 /*
  *     Send an initial eap-tls request to the peer, using the libeap functions.
  */
-static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
+static int mod_session_init(void *type_arg, eap_handler_t *handler)
 {
        int             status;
        tls_session_t   *ssn;
@@ -152,7 +151,6 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
        inst = type_arg;
 
        handler->tls = true;
-       handler->finished = false;
 
        /*
         *      Check if we need a client certificate.
@@ -162,7 +160,7 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
         * EAP-TLS-Require-Client-Cert attribute will override
         * the require_client_cert configuration option.
         */
-       vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
        if (vp) {
                client_cert = vp->vp_integer ? true : false;
        } else {
@@ -197,22 +195,24 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
         *      so rather than hoping the user figures it out,
         *      we force it here.
         */
-       ssn->length_flag = 0;
+       ssn->length_flag = false;
 
        /*
         *      TLS session initialization is over.  Now handle TLS
         *      related handshaking or application data.
         */
        status = eaptls_start(handler->eap_ds, ssn->peap_flag);
-       RDEBUG2("Start returned %d", status);
-       if (status == 0) {
-               return 0;
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
        }
+       if (status == 0) return 0;
 
        /*
         *      The next stage to process the packet.
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
@@ -220,7 +220,7 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
 /*
  *     Do authentication, by letting EAP-TLS do most of the work.
  */
-static int mod_authenticate(void *arg, eap_handler_t *handler)
+static int mod_process(void *arg, eap_handler_t *handler)
 {
        int rcode;
        fr_tls_status_t status;
@@ -238,47 +238,48 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
        }
 
        status = eaptls_process(handler);
-       RDEBUG2("eaptls_process returned %d\n", status);
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       }
+
        switch (status) {
-               /*
-                *      EAP-TLS handshake was successful, tell the
-                *      client to keep talking.
-                *
-                *      If this was EAP-TLS, we would just return
-                *      an EAP-TLS-Success packet here.
-                */
+       /*
+        *      EAP-TLS handshake was successful, tell the
+        *      client to keep talking.
+        *
+        *      If this was EAP-TLS, we would just return
+        *      an EAP-TLS-Success packet here.
+        */
        case FR_TLS_SUCCESS:
-               RDEBUG2("FR_TLS_SUCCESS");
                peap->status = PEAP_STATUS_TUNNEL_ESTABLISHED;
                break;
 
+       /*
+        *      The TLS code is still working on the TLS
+        *      exchange, and it's a valid TLS request.
+        *      do nothing.
+        */
+       case FR_TLS_HANDLED:
                /*
-                *      The TLS code is still working on the TLS
-                *      exchange, and it's a valid TLS request.
-                *      do nothing.
+                *      FIXME: If the SSL session is established, grab the state
+                *      and EAP id from the inner tunnel, and update it with
+                *      the expected EAP id!
                 */
-       case FR_TLS_HANDLED:
-         /*
-          *    FIXME: If the SSL session is established, grab the state
-          *    and EAP id from the inner tunnel, and update it with
-          *    the expected EAP id!
-          */
-               RDEBUG2("FR_TLS_HANDLED");
                return 1;
 
-               /*
-                *      Handshake is done, proceed with decoding tunneled
-                *      data.
-                */
+       /*
+        *      Handshake is done, proceed with decoding tunneled
+        *      data.
+        */
        case FR_TLS_OK:
-               RDEBUG2("FR_TLS_OK");
                break;
 
                /*
                 *      Anything else: fail.
                 */
        default:
-               RDEBUG2("FR_TLS_OTHERS");
                return 0;
        }
 
@@ -318,14 +319,14 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
                if (peap->soh_reply_vps) {
                        RDEBUG2("Using saved attributes from the SoH reply");
                        rdebug_pair_list(L_DBG_LVL_2, request, peap->soh_reply_vps, NULL);
-                       pairfilter(handler->request->reply,
+                       fr_pair_list_mcopy_by_num(handler->request->reply,
                                  &handler->request->reply->vps,
                                  &peap->soh_reply_vps, 0, 0, TAG_ANY);
                }
                if (peap->accept_vps) {
                        RDEBUG2("Using saved attributes from the original Access-Accept");
                        rdebug_pair_list(L_DBG_LVL_2, request, peap->accept_vps, NULL);
-                       pairfilter(handler->request->reply,
+                       fr_pair_list_mcopy_by_num(handler->request->reply,
                                  &handler->request->reply->vps,
                                  &peap->accept_vps, 0, 0, TAG_ANY);
                } else if (peap->use_tunneled_reply) {
@@ -364,10 +365,8 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
  */
 extern rlm_eap_module_t rlm_eap_peap;
 rlm_eap_module_t rlm_eap_peap = {
-       "eap_peap",
-       eappeap_attach,                 /* attach */
-       eappeap_initiate,               /* Start the initial request */
-       NULL,                           /* authorization */
-       mod_authenticate,               /* authentication */
-       NULL                            /* detach */
+       .name           = "eap_peap",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };
index 8cada72..74892b3 100644 (file)
@@ -100,7 +100,7 @@ static void eap_pwd_kdf(uint8_t *key, int keylen, char const *label, int labelle
        }
 }
 
-int compute_password_element (pwd_session_t *sess, uint16_t grp_num,
+int compute_password_element (pwd_session_t *session, uint16_t grp_num,
                              char const *password, int password_len,
                              char const *id_server, int id_server_len,
                              char const *id_peer, int id_peer_len,
@@ -137,43 +137,43 @@ int compute_password_element (pwd_session_t *sess, uint16_t grp_num,
                goto fail;
        }
 
-       sess->pwe = NULL;
-       sess->order = NULL;
-       sess->prime = NULL;
+       session->pwe = NULL;
+       session->order = NULL;
+       session->prime = NULL;
 
-       if ((sess->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
+       if ((session->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
                DEBUG("unable to create EC_GROUP");
                goto fail;
        }
 
        if (((rnd = BN_new()) == NULL) ||
            ((cofactor = BN_new()) == NULL) ||
-           ((sess->pwe = EC_POINT_new(sess->group)) == NULL) ||
-           ((sess->order = BN_new()) == NULL) ||
-           ((sess->prime = BN_new()) == NULL) ||
+           ((session->pwe = EC_POINT_new(session->group)) == NULL) ||
+           ((session->order = BN_new()) == NULL) ||
+           ((session->prime = BN_new()) == NULL) ||
            ((x_candidate = BN_new()) == NULL)) {
                DEBUG("unable to create bignums");
                goto fail;
        }
 
-       if (!EC_GROUP_get_curve_GFp(sess->group, sess->prime, NULL, NULL, NULL)) {
+       if (!EC_GROUP_get_curve_GFp(session->group, session->prime, NULL, NULL, NULL)) {
                DEBUG("unable to get prime for GFp curve");
                goto fail;
        }
 
-       if (!EC_GROUP_get_order(sess->group, sess->order, NULL)) {
+       if (!EC_GROUP_get_order(session->group, session->order, NULL)) {
                DEBUG("unable to get order for curve");
                goto fail;
        }
 
-       if (!EC_GROUP_get_cofactor(sess->group, cofactor, NULL)) {
+       if (!EC_GROUP_get_cofactor(session->group, cofactor, NULL)) {
                DEBUG("unable to get cofactor for curve");
                goto fail;
        }
 
-       primebitlen = BN_num_bits(sess->prime);
-       primebytelen = BN_num_bytes(sess->prime);
-       if ((prfbuf = talloc_zero_array(sess, uint8_t, primebytelen)) == NULL) {
+       primebitlen = BN_num_bits(session->prime);
+       primebytelen = BN_num_bytes(session->prime);
+       if ((prfbuf = talloc_zero_array(session, uint8_t, primebytelen)) == NULL) {
                DEBUG("unable to alloc space for prf buffer");
                goto fail;
        }
@@ -211,7 +211,7 @@ int compute_password_element (pwd_session_t *sess, uint16_t grp_num,
                 * we have to shift right the amount we masked off.
                 */
                if (primebitlen % 8) BN_rshift(x_candidate, x_candidate, (8 - (primebitlen % 8)));
-               if (BN_ucmp(x_candidate, sess->prime) >= 0) continue;
+               if (BN_ucmp(x_candidate, session->prime) >= 0) continue;
 
                /*
                 * need to unambiguously identify the solution, if there is
@@ -223,7 +223,7 @@ int compute_password_element (pwd_session_t *sess, uint16_t grp_num,
                 * solve the quadratic equation, if it's not solvable then we
                 * don't have a point
                 */
-               if (!EC_POINT_set_compressed_coordinates_GFp(sess->group, sess->pwe, x_candidate, is_odd, NULL)) {
+               if (!EC_POINT_set_compressed_coordinates_GFp(session->group, session->pwe, x_candidate, is_odd, NULL)) {
                        continue;
                }
 
@@ -233,20 +233,20 @@ int compute_password_element (pwd_session_t *sess, uint16_t grp_num,
                 * says this is required by X9.62. We're not X9.62 but it can't
                 * hurt just to be sure.
                 */
-               if (!EC_POINT_is_on_curve(sess->group, sess->pwe, NULL)) {
+               if (!EC_POINT_is_on_curve(session->group, session->pwe, NULL)) {
                        DEBUG("EAP-pwd: point is not on curve");
                        continue;
                }
 
                if (BN_cmp(cofactor, BN_value_one())) {
                        /* make sure the point is not in a small sub-group */
-                       if (!EC_POINT_mul(sess->group, sess->pwe, NULL, sess->pwe,
+                       if (!EC_POINT_mul(session->group, session->pwe, NULL, session->pwe,
                                cofactor, NULL)) {
                                DEBUG("EAP-pwd: cannot multiply generator by order");
                                continue;
                        }
 
-                       if (EC_POINT_is_at_infinity(sess->group, sess->pwe)) {
+                       if (EC_POINT_is_at_infinity(session->group, session->pwe)) {
                                DEBUG("EAP-pwd: point is at infinity");
                                continue;
                        }
@@ -255,9 +255,9 @@ int compute_password_element (pwd_session_t *sess, uint16_t grp_num,
                break;
        }
 
-       sess->group_num = grp_num;
+       session->group_num = grp_num;
        if (0) {
-               fail:           /* DON'T free sess, it's in handler->opaque */
+               fail:           /* DON'T free session, it's in handler->opaque */
                ret = -1;
        }
 
@@ -270,35 +270,35 @@ int compute_password_element (pwd_session_t *sess, uint16_t grp_num,
        return ret;
 }
 
-int compute_scalar_element (pwd_session_t *sess, BN_CTX *bnctx) {
+int compute_scalar_element (pwd_session_t *session, BN_CTX *bnctx) {
        BIGNUM *mask = NULL;
        int ret = -1;
 
-       if (((sess->private_value = BN_new()) == NULL) ||
-           ((sess->my_element = EC_POINT_new(sess->group)) == NULL) ||
-           ((sess->my_scalar = BN_new()) == NULL) ||
+       if (((session->private_value = BN_new()) == NULL) ||
+           ((session->my_element = EC_POINT_new(session->group)) == NULL) ||
+           ((session->my_scalar = BN_new()) == NULL) ||
            ((mask = BN_new()) == NULL)) {
                DEBUG2("server scalar allocation failed");
                goto fail;
        }
 
-       if (BN_rand_range(sess->private_value, sess->order) != 1) {
+       if (BN_rand_range(session->private_value, session->order) != 1) {
                DEBUG2("Unable to get randomness for private_value");
                goto fail;
        }
-       if (BN_rand_range(mask, sess->order) != 1) {
+       if (BN_rand_range(mask, session->order) != 1) {
                DEBUG2("Unable to get randomness for mask");
                goto fail;
        }
-       BN_add(sess->my_scalar, sess->private_value, mask);
-       BN_mod(sess->my_scalar, sess->my_scalar, sess->order, bnctx);
+       BN_add(session->my_scalar, session->private_value, mask);
+       BN_mod(session->my_scalar, session->my_scalar, session->order, bnctx);
 
-       if (!EC_POINT_mul(sess->group, sess->my_element, NULL, sess->pwe, mask, bnctx)) {
+       if (!EC_POINT_mul(session->group, session->my_element, NULL, session->pwe, mask, bnctx)) {
                DEBUG2("server element allocation failed");
                goto fail;
        }
 
-       if (!EC_POINT_invert(sess->group, sess->my_element, bnctx)) {
+       if (!EC_POINT_invert(session->group, session->my_element, bnctx)) {
                DEBUG2("server element inversion failed");
                goto fail;
        }
@@ -311,67 +311,80 @@ fail:
        return ret;
 }
 
-int process_peer_commit (pwd_session_t *sess, uint8_t *commit, BN_CTX *bnctx)
+int process_peer_commit (pwd_session_t *session, uint8_t *in, size_t in_len, BN_CTX *bnctx)
 {
        uint8_t *ptr;
+       size_t data_len;
        BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
        EC_POINT *K = NULL, *point = NULL;
        int res = 1;
 
-       if (((sess->peer_scalar = BN_new()) == NULL) ||
-           ((sess->k = BN_new()) == NULL) ||
+       if (((session->peer_scalar = BN_new()) == NULL) ||
+           ((session->k = BN_new()) == NULL) ||
            ((cofactor = BN_new()) == NULL) ||
            ((x = BN_new()) == NULL) ||
            ((y = BN_new()) == NULL) ||
-           ((point = EC_POINT_new(sess->group)) == NULL) ||
-           ((K = EC_POINT_new(sess->group)) == NULL) ||
-           ((sess->peer_element = EC_POINT_new(sess->group)) == NULL)) {
+           ((point = EC_POINT_new(session->group)) == NULL) ||
+           ((K = EC_POINT_new(session->group)) == NULL) ||
+           ((session->peer_element = EC_POINT_new(session->group)) == NULL)) {
                DEBUG2("pwd: failed to allocate room to process peer's commit");
                goto finish;
        }
 
-       if (!EC_GROUP_get_cofactor(sess->group, cofactor, NULL)) {
+       if (!EC_GROUP_get_cofactor(session->group, cofactor, NULL)) {
                DEBUG2("pwd: unable to get group co-factor");
                goto finish;
        }
 
        /* element, x then y, followed by scalar */
-       ptr = (uint8_t *)commit;
-       BN_bin2bn(ptr, BN_num_bytes(sess->prime), x);
-       ptr += BN_num_bytes(sess->prime);
-       BN_bin2bn(ptr, BN_num_bytes(sess->prime), y);
-       ptr += BN_num_bytes(sess->prime);
-       BN_bin2bn(ptr, BN_num_bytes(sess->order), sess->peer_scalar);
-
-       if (!EC_POINT_set_affine_coordinates_GFp(sess->group, sess->peer_element, x, y, bnctx)) {
+       ptr = (uint8_t *)in;
+       data_len = BN_num_bytes(session->prime);
+
+       /*
+        *      Did the peer send enough data?
+        */
+       if (in_len < (2 * data_len + BN_num_bytes(session->order))) {
+               DEBUG("pwd: Invalid commit packet");
+               goto finish;
+       }
+
+       BN_bin2bn(ptr, data_len, x);
+       ptr += data_len;
+       BN_bin2bn(ptr, data_len, y);
+       ptr += data_len;
+
+       data_len = BN_num_bytes(session->order);
+       BN_bin2bn(ptr, data_len, session->peer_scalar);
+
+       if (!EC_POINT_set_affine_coordinates_GFp(session->group, session->peer_element, x, y, bnctx)) {
                DEBUG2("pwd: unable to get coordinates of peer's element");
                goto finish;
        }
 
        /* check to ensure peer's element is not in a small sub-group */
        if (BN_cmp(cofactor, BN_value_one())) {
-               if (!EC_POINT_mul(sess->group, point, NULL, sess->peer_element, cofactor, NULL)) {
+               if (!EC_POINT_mul(session->group, point, NULL, session->peer_element, cofactor, NULL)) {
                        DEBUG2("pwd: unable to multiply element by co-factor");
                        goto finish;
                }
 
-               if (EC_POINT_is_at_infinity(sess->group, point)) {
+               if (EC_POINT_is_at_infinity(session->group, point)) {
                        DEBUG2("pwd: peer's element is in small sub-group");
                        goto finish;
                }
        }
 
        /* compute the shared key, k */
-       if ((!EC_POINT_mul(sess->group, K, NULL, sess->pwe, sess->peer_scalar, bnctx)) ||
-           (!EC_POINT_add(sess->group, K, K, sess->peer_element, bnctx)) ||
-           (!EC_POINT_mul(sess->group, K, NULL, K, sess->private_value, bnctx))) {
+       if ((!EC_POINT_mul(session->group, K, NULL, session->pwe, session->peer_scalar, bnctx)) ||
+           (!EC_POINT_add(session->group, K, K, session->peer_element, bnctx)) ||
+           (!EC_POINT_mul(session->group, K, NULL, K, session->private_value, bnctx))) {
                DEBUG2("pwd: unable to compute shared key, k");
                goto finish;
        }
 
        /* ensure that the shared key isn't in a small sub-group */
        if (BN_cmp(cofactor, BN_value_one())) {
-               if (!EC_POINT_mul(sess->group, K, NULL, K, cofactor, NULL)) {
+               if (!EC_POINT_mul(session->group, K, NULL, K, cofactor, NULL)) {
                        DEBUG2("pwd: unable to multiply k by co-factor");
                        goto finish;
                }
@@ -383,12 +396,12 @@ int process_peer_commit (pwd_session_t *sess, uint8_t *commit, BN_CTX *bnctx)
         * never going to happen it is a simple and safe check "just to be
         * sure" so let's be safe.
         */
-       if (EC_POINT_is_at_infinity(sess->group, K)) {
+       if (EC_POINT_is_at_infinity(session->group, K)) {
                DEBUG2("pwd: k is point-at-infinity!");
                goto finish;
        }
 
-       if (!EC_POINT_get_affine_coordinates_GFp(sess->group, K, sess->k, NULL, bnctx)) {
+       if (!EC_POINT_get_affine_coordinates_GFp(session->group, K, session->k, NULL, bnctx)) {
                DEBUG2("pwd: unable to get shared secret from K");
                goto finish;
        }
@@ -404,7 +417,7 @@ finish:
        return res;
 }
 
-int compute_server_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
+int compute_server_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx)
 {
        BIGNUM *x = NULL, *y = NULL;
        HMAC_CTX ctx;
@@ -414,7 +427,7 @@ int compute_server_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
        /*
         * Each component of the cruft will be at most as big as the prime
         */
-       if (((cruft = talloc_zero_array(sess, uint8_t, BN_num_bytes(sess->prime))) == NULL) ||
+       if (((cruft = talloc_zero_array(session, uint8_t, BN_num_bytes(session->prime))) == NULL) ||
            ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
                DEBUG2("pwd: unable to allocate space to compute confirm!");
                goto finish;
@@ -432,67 +445,67 @@ int compute_server_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
         *
         * First is k
         */
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(sess->k);
-       BN_bn2bin(sess->k, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(session->k);
+       BN_bn2bin(session->k, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
        /*
         * next is server element: x, y
         */
-       if (!EC_POINT_get_affine_coordinates_GFp(sess->group, sess->my_element, x, y, bnctx)) {
+       if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->my_element, x, y, bnctx)) {
                DEBUG2("pwd: unable to get coordinates of server element");
                goto finish;
        }
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(x);
        BN_bn2bin(x, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(y);
        BN_bn2bin(y, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
        /*
         * and server scalar
         */
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->my_scalar);
-       BN_bn2bin(sess->my_scalar, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->order));
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar);
+       BN_bn2bin(session->my_scalar, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->order));
 
        /*
         * next is peer element: x, y
         */
-       if (!EC_POINT_get_affine_coordinates_GFp(sess->group, sess->peer_element, x, y, bnctx)) {
+       if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->peer_element, x, y, bnctx)) {
                DEBUG2("pwd: unable to get coordinates of peer's element");
                goto finish;
        }
 
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(x);
        BN_bn2bin(x, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(y);
        BN_bn2bin(y, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
        /*
         * and peer scalar
         */
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->peer_scalar);
-       BN_bn2bin(sess->peer_scalar, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->order));
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->order) - BN_num_bytes(session->peer_scalar);
+       BN_bn2bin(session->peer_scalar, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->order));
 
        /*
         * finally, ciphersuite
         */
-       H_Update(&ctx, (uint8_t *)&sess->ciphersuite, sizeof(sess->ciphersuite));
+       H_Update(&ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite));
 
-       H_Final(&ctx, buf);
+       H_Final(&ctx, out);
 
        req = 0;
 finish:
@@ -503,7 +516,7 @@ finish:
        return req;
 }
 
-int compute_peer_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
+int compute_peer_confirm (pwd_session_t *session, uint8_t *out, BN_CTX *bnctx)
 {
        BIGNUM *x = NULL, *y = NULL;
        HMAC_CTX ctx;
@@ -513,7 +526,7 @@ int compute_peer_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
        /*
         * Each component of the cruft will be at most as big as the prime
         */
-       if (((cruft = talloc_zero_array(sess, uint8_t, BN_num_bytes(sess->prime))) == NULL) ||
+       if (((cruft = talloc_zero_array(session, uint8_t, BN_num_bytes(session->prime))) == NULL) ||
            ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
                DEBUG2("pwd: unable to allocate space to compute confirm!");
                goto finish;
@@ -531,67 +544,67 @@ int compute_peer_confirm (pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx)
         *
         * First is k
         */
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(sess->k);
-       BN_bn2bin(sess->k, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(session->k);
+       BN_bn2bin(session->k, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
        /*
        * then peer element: x, y
        */
-       if (!EC_POINT_get_affine_coordinates_GFp(sess->group, sess->peer_element, x, y, bnctx)) {
+       if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->peer_element, x, y, bnctx)) {
                DEBUG2("pwd: unable to get coordinates of peer's element");
                goto finish;
        }
 
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(x);
        BN_bn2bin(x, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(y);
        BN_bn2bin(y, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
        /*
         * and peer scalar
         */
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->peer_scalar);
-       BN_bn2bin(sess->peer_scalar, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->order));
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->order) - BN_num_bytes(session->peer_scalar);
+       BN_bn2bin(session->peer_scalar, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->order));
 
        /*
         * then server element: x, y
         */
-       if (!EC_POINT_get_affine_coordinates_GFp(sess->group, sess->my_element, x, y, bnctx)) {
+       if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->my_element, x, y, bnctx)) {
                DEBUG2("pwd: unable to get coordinates of server element");
                goto finish;
        }
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(x);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(x);
        BN_bn2bin(x, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(y);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(y);
        BN_bn2bin(y, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
        /*
         * and server scalar
         */
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->my_scalar);
-       BN_bn2bin(sess->my_scalar, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->order));
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar);
+       BN_bn2bin(session->my_scalar, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->order));
 
        /*
         * finally, ciphersuite
         */
-       H_Update(&ctx, (uint8_t *)&sess->ciphersuite, sizeof(sess->ciphersuite));
+       H_Update(&ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite));
 
-       H_Final(&ctx, buf);
+       H_Final(&ctx, out);
 
        req = 0;
 finish:
@@ -602,7 +615,7 @@ finish:
        return req;
 }
 
-int compute_keys (pwd_session_t *sess, uint8_t *peer_confirm, uint8_t *msk, uint8_t *emsk)
+int compute_keys (pwd_session_t *session, uint8_t *peer_confirm, uint8_t *msk, uint8_t *emsk)
 {
        HMAC_CTX ctx;
        uint8_t mk[SHA256_DIGEST_LENGTH], *cruft;
@@ -610,7 +623,7 @@ int compute_keys (pwd_session_t *sess, uint8_t *peer_confirm, uint8_t *msk, uint
        uint8_t msk_emsk[128];          /* 64 each */
        int offset;
 
-       if ((cruft = talloc_array(sess, uint8_t, BN_num_bytes(sess->prime))) == NULL) {
+       if ((cruft = talloc_array(session, uint8_t, BN_num_bytes(session->prime))) == NULL) {
                DEBUG2("pwd: unable to allocate space to compute keys");
                return -1;
        }
@@ -621,28 +634,28 @@ int compute_keys (pwd_session_t *sess, uint8_t *peer_confirm, uint8_t *msk, uint
         */
        session_id[0] = PW_EAP_PWD;
        H_Init(&ctx);
-       H_Update(&ctx, (uint8_t *)&sess->ciphersuite, sizeof(sess->ciphersuite));
-       offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->peer_scalar);
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       BN_bn2bin(sess->peer_scalar, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->order));
-       offset = BN_num_bytes(sess->order) - BN_num_bytes(sess->my_scalar);
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       BN_bn2bin(sess->my_scalar, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->order));
+       H_Update(&ctx, (uint8_t *)&session->ciphersuite, sizeof(session->ciphersuite));
+       offset = BN_num_bytes(session->order) - BN_num_bytes(session->peer_scalar);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       BN_bn2bin(session->peer_scalar, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->order));
+       offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar);
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       BN_bn2bin(session->my_scalar, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->order));
        H_Final(&ctx, (uint8_t *)&session_id[1]);
 
        /* then compute MK = H(k | commit-peer | commit-server) */
        H_Init(&ctx);
 
-       memset(cruft, 0, BN_num_bytes(sess->prime));
-       offset = BN_num_bytes(sess->prime) - BN_num_bytes(sess->k);
-       BN_bn2bin(sess->k, cruft + offset);
-       H_Update(&ctx, cruft, BN_num_bytes(sess->prime));
+       memset(cruft, 0, BN_num_bytes(session->prime));
+       offset = BN_num_bytes(session->prime) - BN_num_bytes(session->k);
+       BN_bn2bin(session->k, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(session->prime));
 
        H_Update(&ctx, peer_confirm, SHA256_DIGEST_LENGTH);
 
-       H_Update(&ctx, sess->my_confirm, SHA256_DIGEST_LENGTH);
+       H_Update(&ctx, session->my_confirm, SHA256_DIGEST_LENGTH);
 
        H_Final(&ctx, mk);
 
index 663e3e3..013a6e7 100644 (file)
@@ -71,7 +71,7 @@ typedef struct _pwd_id_packet {
 #define EAP_PWD_PREP_MS                1
 #define EAP_PWD_PREP_SASL      2
     char identity[];
-} CC_HINT(packed) pwd_id_packet;
+} CC_HINT(packed) pwd_id_packet_t;
 
 typedef struct _pwd_session_t {
     uint16_t state;
@@ -83,13 +83,13 @@ typedef struct _pwd_session_t {
     uint32_t token;
     char peer_id[MAX_STRING_LEN];
     size_t peer_id_len;
-    int mtu;
-    uint8_t *in_buf;      /* reassembled fragments */
-    size_t in_buf_pos;
-    size_t in_buf_len;
-    uint8_t *out_buf;     /* message to fragment */
-    size_t out_buf_pos;
-    size_t out_buf_len;
+    size_t mtu;
+    uint8_t *in;      /* reassembled fragments */
+    size_t in_pos;
+    size_t in_len;
+    uint8_t *out;     /* message to fragment */
+    size_t out_pos;
+    size_t out_len;
     EC_GROUP *group;
     EC_POINT *pwe;
     BIGNUM *order;
@@ -109,9 +109,9 @@ int compute_password_element(pwd_session_t *sess, uint16_t grp_num,
                             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);
-int compute_server_confirm(pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx);
-int compute_peer_confirm(pwd_session_t *sess, uint8_t *buf, BN_CTX *bnctx);
+int process_peer_commit (pwd_session_t *sess, uint8_t *in, size_t in_len, BN_CTX *bnctx);
+int compute_server_confirm(pwd_session_t *sess, uint8_t *out, BN_CTX *bnctx);
+int compute_peer_confirm(pwd_session_t *sess, uint8_t *out, BN_CTX *bnctx);
 int compute_keys(pwd_session_t *sess, uint8_t *peer_confirm,
                 uint8_t *msk, uint8_t *emsk);
 #ifdef PRINTBUF
index 08950da..6220bbd 100644 (file)
@@ -38,15 +38,15 @@ USES_APPLE_DEPRECATED_API   /* OpenSSL API has been deprecated by Apple */
 
 #include "eap_pwd.h"
 
-#define MSK_EMSK_LEN    64
 #define MPPE_KEY_LEN    32
+#define MSK_EMSK_LEN    (2*MPPE_KEY_LEN)
 
 static CONF_PARSER pwd_module_config[] = {
-       { "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 }
+       { "group", FR_CONF_OFFSET(PW_TYPE_INTEGER, eap_pwd_t, group), "19" },
+       { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, eap_pwd_t, fragment_size), "1020" },
+       { "server_id", FR_CONF_OFFSET(PW_TYPE_STRING, eap_pwd_t, server_id), NULL },
+       { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, eap_pwd_t, virtual_server), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 static int mod_detach (void *arg)
@@ -60,22 +60,24 @@ static int mod_detach (void *arg)
        return 0;
 }
 
-static int eap_pwd_attach (CONF_SECTION *cs, void **instance)
+static int mod_instantiate (CONF_SECTION *cs, void **instance)
 {
        eap_pwd_t *inst;
 
        *instance = inst = talloc_zero(cs, eap_pwd_t);
        if (!inst) return -1;
 
-       inst->conf = talloc_zero(inst, EAP_PWD_CONF);
-       if (!inst->conf) return -1;
+       if (cf_section_parse(cs, inst, pwd_module_config) < 0) {
+               return -1;
+       }
 
-       if (cf_section_parse(cs, inst->conf, pwd_module_config) < 0) {
+       if (inst->fragment_size < 100) {
+               cf_log_err_cs(cs, "Fragment size is too small");
                return -1;
        }
 
        if ((inst->bnctx = BN_CTX_new()) == NULL) {
-               ERROR("rlm_eap_pwd: Failed to get BN context");
+               cf_log_err_cs(cs, "Failed to get BN context");
                return -1;
        }
 
@@ -98,21 +100,21 @@ static int _free_pwd_session (pwd_session_t *session)
        return 0;
 }
 
-static int send_pwd_request (pwd_session_t *sess, EAP_DS *eap_ds)
+static int send_pwd_request (pwd_session_t *session, EAP_DS *eap_ds)
 {
-       int len;
+       size_t len;
        uint16_t totlen;
        pwd_hdr *hdr;
 
-       len = (sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr);
+       len = (session->out_len - session->out_pos) + sizeof(pwd_hdr);
        rad_assert(len > 0);
        eap_ds->request->code = PW_EAP_REQUEST;
        eap_ds->request->type.num = PW_EAP_PWD;
-       eap_ds->request->type.length = (len > sess->mtu) ? sess->mtu : len;
+       eap_ds->request->type.length = (len > session->mtu) ? session->mtu : len;
        eap_ds->request->type.data = talloc_zero_array(eap_ds->request, uint8_t, eap_ds->request->type.length);
        hdr = (pwd_hdr *)eap_ds->request->type.data;
 
-       switch (sess->state) {
+       switch (session->state) {
        case PWD_STATE_ID_REQ:
                EAP_PWD_SET_EXCHANGE(hdr, EAP_PWD_EXCH_ID);
                break;
@@ -132,60 +134,60 @@ static int send_pwd_request (pwd_session_t *sess, EAP_DS *eap_ds)
        /*
         * are we fragmenting?
         */
-       if ((int)((sess->out_buf_len - sess->out_buf_pos) + sizeof(pwd_hdr)) > sess->mtu) {
+       if (((session->out_len - session->out_pos) + sizeof(pwd_hdr)) > session->mtu) {
                EAP_PWD_SET_MORE_BIT(hdr);
-               if (sess->out_buf_pos == 0) {
+               if (session->out_pos == 0) {
                        /*
                         * the first fragment, add the total length
                         */
                        EAP_PWD_SET_LENGTH_BIT(hdr);
-                       totlen = ntohs(sess->out_buf_len);
+                       totlen = ntohs(session->out_len);
                        memcpy(hdr->data, (char *)&totlen, sizeof(totlen));
                        memcpy(hdr->data + sizeof(uint16_t),
-                       sess->out_buf,
-                       sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
-                       sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
+                              session->out,
+                              session->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
+                       session->out_pos += (session->mtu - sizeof(pwd_hdr) - sizeof(uint16_t));
                } else {
                        /*
                         * an intermediate fragment
                         */
-                       memcpy(hdr->data, sess->out_buf + sess->out_buf_pos, (sess->mtu - sizeof(pwd_hdr)));
-                       sess->out_buf_pos += (sess->mtu - sizeof(pwd_hdr));
+                       memcpy(hdr->data, session->out + session->out_pos, (session->mtu - sizeof(pwd_hdr)));
+                       session->out_pos += (session->mtu - sizeof(pwd_hdr));
                }
        } else {
                /*
                 * either it's not a fragment or it's the last fragment.
                 * The out buffer isn't needed anymore though so get rid of it.
                 */
-               memcpy(hdr->data, sess->out_buf + sess->out_buf_pos,
-               (sess->out_buf_len - sess->out_buf_pos));
-               talloc_free(sess->out_buf);
-               sess->out_buf = NULL;
-               sess->out_buf_pos = sess->out_buf_len = 0;
+               memcpy(hdr->data, session->out + session->out_pos,
+               (session->out_len - session->out_pos));
+               talloc_free(session->out);
+               session->out = NULL;
+               session->out_pos = session->out_len = 0;
        }
        return 1;
 }
 
-static int eap_pwd_initiate (void *instance, eap_handler_t *handler)
+static int mod_session_init (void *instance, eap_handler_t *handler)
 {
-       pwd_session_t *pwd_session;
+       pwd_session_t *session;
        eap_pwd_t *inst = (eap_pwd_t *)instance;
        VALUE_PAIR *vp;
-       pwd_id_packet *pack;
+       pwd_id_packet_t *packet;
 
        if (!inst || !handler) {
                ERROR("rlm_eap_pwd: Initiate, NULL data provided");
-               return -1;
+               return 0;
        }
 
        /*
        * make sure the server's been configured properly
        */
-       if (!inst->conf->server_id) {
+       if (!inst->server_id) {
                ERROR("rlm_eap_pwd: Server ID is not configured");
-               return -1;
+               return 0;
        }
-       switch (inst->conf->group) {
+       switch (inst->group) {
        case 19:
        case 20:
        case 21:
@@ -195,104 +197,115 @@ static int eap_pwd_initiate (void *instance, eap_handler_t *handler)
 
        default:
                ERROR("rlm_eap_pwd: Group is not supported");
-               return -1;
+               return 0;
        }
 
-       if ((pwd_session = talloc_zero(handler, pwd_session_t)) == NULL) return -1;
-       talloc_set_destructor(pwd_session, _free_pwd_session);
+       if ((session = talloc_zero(handler, pwd_session_t)) == NULL) return 0;
+       talloc_set_destructor(session, _free_pwd_session);
        /*
         * set things up so they can be free'd reliably
         */
-       pwd_session->group_num = inst->conf->group;
-       pwd_session->private_value = NULL;
-       pwd_session->peer_scalar = NULL;
-       pwd_session->my_scalar = NULL;
-       pwd_session->k = NULL;
-       pwd_session->my_element = NULL;
-       pwd_session->peer_element = NULL;
-       pwd_session->group = NULL;
-       pwd_session->pwe = NULL;
-       pwd_session->order = NULL;
-       pwd_session->prime = NULL;
+       session->group_num = inst->group;
+       session->private_value = NULL;
+       session->peer_scalar = NULL;
+       session->my_scalar = NULL;
+       session->k = NULL;
+       session->my_element = NULL;
+       session->peer_element = NULL;
+       session->group = NULL;
+       session->pwe = NULL;
+       session->order = NULL;
+       session->prime = NULL;
 
        /*
-       * figure out the MTU (basically do what eap-tls does)
-       */
-       pwd_session->mtu = inst->conf->fragment_size;
-       vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
+        *      The admin can dynamically change the MTU.
+        */
+       session->mtu = inst->fragment_size;
+       vp = fr_pair_find_by_num(handler->request->packet->vps, PW_FRAMED_MTU, 0, TAG_ANY);
 
        /*
-        * 9 = 4 (EAPOL header) + 4 (EAP header) + 1 (EAP type)
+        *      session->mtu is *our* MTU.  We need to subtract off the EAP
+        *      overhead.
         *
-        * the fragmentation code deals with the included length
-        * so we don't need to subtract that here.
+        *      9 = 4 (EAPOL header) + 4 (EAP header) + 1 (EAP type)
+        *
+        *      The fragmentation code deals with the included length
+        *      so we don't need to subtract that here.
         */
-       if (vp && ((int)(vp->vp_integer - 9) < pwd_session->mtu)) {
-               pwd_session->mtu = vp->vp_integer - 9;
+       if (vp && (vp->vp_integer > 100) && (vp->vp_integer < session->mtu)) {
+               session->mtu = vp->vp_integer - 9;
        }
 
-       pwd_session->state = PWD_STATE_ID_REQ;
-       pwd_session->in_buf = NULL;
-       pwd_session->out_buf_pos = 0;
-       handler->opaque = pwd_session;
+       session->state = PWD_STATE_ID_REQ;
+       session->in = NULL;
+       session->out_pos = 0;
+       handler->opaque = session;
 
        /*
         * construct an EAP-pwd-ID/Request
         */
-       pwd_session->out_buf_len = sizeof(pwd_id_packet) + strlen(inst->conf->server_id);
-       if ((pwd_session->out_buf = talloc_zero_array(pwd_session, uint8_t, pwd_session->out_buf_len)) == NULL) {
-               return -1;
+       session->out_len = sizeof(pwd_id_packet_t) + strlen(inst->server_id);
+       if ((session->out = talloc_zero_array(session, uint8_t, session->out_len)) == NULL) {
+               return 0;
        }
 
-       pack = (pwd_id_packet *)pwd_session->out_buf;
-       pack->group_num = htons(pwd_session->group_num);
-       pack->random_function = EAP_PWD_DEF_RAND_FUN;
-       pack->prf = EAP_PWD_DEF_PRF;
-       pwd_session->token = fr_rand();
-       memcpy(pack->token, (char *)&pwd_session->token, 4);
-       pack->prep = EAP_PWD_PREP_NONE;
-       strcpy(pack->identity, inst->conf->server_id);
+       packet = (pwd_id_packet_t *)session->out;
+       packet->group_num = htons(session->group_num);
+       packet->random_function = EAP_PWD_DEF_RAND_FUN;
+       packet->prf = EAP_PWD_DEF_PRF;
+       session->token = fr_rand();
+       memcpy(packet->token, (char *)&session->token, 4);
+       packet->prep = EAP_PWD_PREP_NONE;
+       memcpy(packet->identity, inst->server_id, session->out_len - sizeof(pwd_id_packet_t) );
 
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
-       return send_pwd_request(pwd_session, handler->eap_ds);
+       return send_pwd_request(session, handler->eap_ds);
 }
 
-static int mod_authenticate (void *arg, eap_handler_t *handler)
+static int mod_process(void *arg, eap_handler_t *handler)
 {
-       pwd_session_t *pwd_session;
+       pwd_session_t *session;
        pwd_hdr *hdr;
-       pwd_id_packet *id;
+       pwd_id_packet_t *packet;
        eap_packet_t *response;
        REQUEST *request, *fake;
        VALUE_PAIR *pw, *vp;
        EAP_DS *eap_ds;
-       int len, ret = 0;
+       size_t in_len;
+       int ret = 0;
        eap_pwd_t *inst = (eap_pwd_t *)arg;
        uint16_t offset;
-       uint8_t exch, *buf, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN];
+       uint8_t exch, *in, *ptr, msk[MSK_EMSK_LEN], emsk[MSK_EMSK_LEN];
        uint8_t peer_confirm[SHA256_DIGEST_LENGTH];
        BIGNUM *x = NULL, *y = NULL;
        char *p;
 
-       if (!handler || ((eap_ds = handler->eap_ds) == NULL) || !inst) return 0;
+       if (((eap_ds = handler->eap_ds) == NULL) || !inst) return 0;
 
-       pwd_session = (pwd_session_t *)handler->opaque;
+       session = (pwd_session_t *)handler->opaque;
        request = handler->request;
        response = handler->eap_ds->response;
        hdr = (pwd_hdr *)response->type.data;
 
-       buf = hdr->data;
-       len = response->type.length - sizeof(pwd_hdr);
+       /*
+        *      The header must be at least one byte.
+        */
+       if (!hdr || (response->type.length < sizeof(pwd_hdr))) {
+               RDEBUG("Packet with insufficient data");
+               return 0;
+       }
+
+       in = hdr->data;
+       in_len = response->type.length - sizeof(pwd_hdr);
 
        /*
        * see if we're fragmenting, if so continue until we're done
        */
-       if (pwd_session->out_buf_pos) {
-               if (len) {
-                       RDEBUG2("pwd got something more than an ACK for a fragment");
-               }
-               return send_pwd_request(pwd_session, eap_ds);
+       if (session->out_pos) {
+               if (in_len) RDEBUG2("pwd got something more than an ACK for a fragment");
+
+               return send_pwd_request(session, eap_ds);
        }
 
        /*
@@ -300,20 +313,26 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
        * buffer to hold all the fragments
        */
        if (EAP_PWD_GET_LENGTH_BIT(hdr)) {
-               if (pwd_session->in_buf) {
+               if (session->in) {
                        RDEBUG2("pwd already alloced buffer for fragments");
                        return 0;
                }
-               pwd_session->in_buf_len = ntohs(buf[0] * 256 | buf[1]);
-               if ((pwd_session->in_buf = talloc_zero_array(pwd_session, uint8_t, pwd_session->in_buf_len)) == NULL) {
+
+               if (in_len < 2) {
+                       RDEBUG("Invalid packet: length bit set, but no length field");
+                       return 0;
+               }
+
+               session->in_len = ntohs(in[0] * 256 | in[1]);
+               if ((session->in = talloc_zero_array(session, uint8_t, session->in_len)) == NULL) {
                        RDEBUG2("pwd cannot allocate %zd buffer to hold fragments",
-                               pwd_session->in_buf_len);
+                               session->in_len);
                        return 0;
                }
-               memset(pwd_session->in_buf, 0, pwd_session->in_buf_len);
-               pwd_session->in_buf_pos = 0;
-               buf += sizeof(uint16_t);
-               len -= sizeof(uint16_t);
+               memset(session->in, 0, session->in_len);
+               session->in_pos = 0;
+               in += sizeof(uint16_t);
+               in_len -= sizeof(uint16_t);
        }
 
        /*
@@ -321,14 +340,15 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
         * buffer those fragments!
         */
        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");
+               rad_assert(session->in != NULL);
+
+               if ((session->in_pos + in_len) > session->in_len) {
+                       RDEBUG2("Fragment overflows packet.");
                        return 0;
                }
 
-               memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
-               pwd_session->in_buf_pos += len;
+               memcpy(session->in + session->in_pos, in, in_len);
+               session->in_pos += in_len;
 
                /*
                 * send back an ACK for this fragment
@@ -346,54 +366,58 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
        }
 
 
-       if (pwd_session->in_buf) {
+       if (session->in) {
                /*
                 * the last fragment...
                 */
-               if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
+               if ((session->in_pos + in_len) > session->in_len) {
                        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);
-               buf = pwd_session->in_buf;
-               len = pwd_session->in_buf_len;
+               memcpy(session->in + session->in_pos, in, in_len);
+               in = session->in;
+               in_len = session->in_len;
        }
 
-       switch (pwd_session->state) {
+       switch (session->state) {
        case PWD_STATE_ID_REQ:
                if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_ID) {
                        RDEBUG2("pwd exchange is incorrect: not ID");
                        return 0;
                }
 
-               id = (pwd_id_packet *)buf;
-               if ((id->prf != EAP_PWD_DEF_PRF) ||
-                   (id->random_function != EAP_PWD_DEF_RAND_FUN) ||
-                   (id->prep != EAP_PWD_PREP_NONE) ||
-                   (CRYPTO_memcmp(id->token, (char *)&pwd_session->token, 4)) ||
-                   (id->group_num != ntohs(pwd_session->group_num))) {
+               packet = (pwd_id_packet_t *) in;
+               if (in_len < sizeof(packet)) {
+                       RDEBUG("Packet is too small (%zd < %zd).", in_len, sizeof(packet));
+                       return 0;
+               }
+
+               if ((packet->prf != EAP_PWD_DEF_PRF) ||
+                   (packet->random_function != EAP_PWD_DEF_RAND_FUN) ||
+                   (packet->prep != EAP_PWD_PREP_NONE) ||
+                   (CRYPTO_memcmp(packet->token, &session->token, 4)) ||
+                   (packet->group_num != ntohs(session->group_num))) {
                        RDEBUG2("pwd id response is invalid");
                        return 0;
                }
                /*
                 * we've agreed on the ciphersuite, record it...
                 */
-               ptr = (uint8_t *)&pwd_session->ciphersuite;
-               memcpy(ptr, (char *)&id->group_num, sizeof(uint16_t));
+               ptr = (uint8_t *)&session->ciphersuite;
+               memcpy(ptr, (char *)&packet->group_num, sizeof(uint16_t));
                ptr += sizeof(uint16_t);
                *ptr = EAP_PWD_DEF_RAND_FUN;
                ptr += sizeof(uint8_t);
                *ptr = EAP_PWD_DEF_PRF;
 
-               pwd_session->peer_id_len = len - sizeof(pwd_id_packet);
-               if (pwd_session->peer_id_len >= sizeof(pwd_session->peer_id)) {
+               session->peer_id_len = in_len - sizeof(pwd_id_packet_t);
+               if (session->peer_id_len >= sizeof(session->peer_id)) {
                        RDEBUG2("pwd id response is malformed");
                        return 0;
                }
 
-               memcpy(pwd_session->peer_id, id->identity,
-               pwd_session->peer_id_len);
-               pwd_session->peer_id[pwd_session->peer_id_len] = '\0';
+               memcpy(session->peer_id, packet->identity, session->peer_id_len);
+               session->peer_id[session->peer_id_len] = '\0';
 
                /*
                 * make fake request to get the password for the usable ID
@@ -402,23 +426,23 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                        RDEBUG("pwd unable to create fake request!");
                        return 0;
                }
-               fake->username = pairmake_packet("User-Name", NULL, T_OP_EQ);
+               fake->username = pair_make_request("User-Name", NULL, T_OP_EQ);
                if (!fake->username) {
                        RDEBUG("pwd unanable to create value pair for username!");
                        talloc_free(fake);
                        return 0;
                }
-               fake->username->vp_length = pwd_session->peer_id_len;
+               fake->username->vp_length = session->peer_id_len;
                fake->username->vp_strvalue = p = talloc_array(fake->username, char, fake->username->vp_length + 1);
+               memcpy(p, session->peer_id, session->peer_id_len);
+               p[fake->username->vp_length] = '\0';
 
-               memcpy(p, pwd_session->peer_id,
-               pwd_session->peer_id_len);
-               p[fake->username->vp_length] = 0;
+               fr_pair_add(&fake->packet->vps, fake->username);
 
-               if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
                        fake->server = vp->vp_strvalue;
-               } else if (inst->conf->virtual_server) {
-                       fake->server = inst->conf->virtual_server;
+               } else if (inst->virtual_server) {
+                       fake->server = inst->virtual_server;
                } /* else fake->server == request->server */
 
                RDEBUG("Sending tunneled request");
@@ -451,18 +475,18 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                RDEBUG("Got tunneled reply code %d", fake->reply->code);
                rdebug_pair_list(L_DBG_LVL_1, request, fake->reply->vps, NULL);
 
-               if ((pw = pairfind(fake->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) {
+               if ((pw = fr_pair_find_by_num(fake->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) == NULL) {
                        DEBUG2("failed to find password for %s to do pwd authentication",
-                       pwd_session->peer_id);
+                       session->peer_id);
                        talloc_free(fake);
                        return 0;
                }
 
-               if (compute_password_element(pwd_session, pwd_session->group_num,
+               if (compute_password_element(session, session->group_num,
                                             pw->data.strvalue, strlen(pw->data.strvalue),
-                                            inst->conf->server_id, strlen(inst->conf->server_id),
-                                            pwd_session->peer_id, strlen(pwd_session->peer_id),
-                                            &pwd_session->token)) {
+                                            inst->server_id, strlen(inst->server_id),
+                                            session->peer_id, strlen(session->peer_id),
+                                            &session->token)) {
                        DEBUG2("failed to obtain password element");
                        talloc_free(fake);
                        return 0;
@@ -472,7 +496,7 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                /*
                 * compute our scalar and element
                 */
-               if (compute_scalar_element(pwd_session, inst->bnctx)) {
+               if (compute_scalar_element(session, inst->bnctx)) {
                        DEBUG2("failed to compute server's scalar and element");
                        return 0;
                }
@@ -485,7 +509,7 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                /*
                 * element is a point, get both coordinates: x and y
                 */
-               if (!EC_POINT_get_affine_coordinates_GFp(pwd_session->group, pwd_session->my_element, x, y,
+               if (!EC_POINT_get_affine_coordinates_GFp(session->group, session->my_element, x, y,
                                                         inst->bnctx)) {
                        DEBUG2("server point assignment failed");
                        BN_clear_free(x);
@@ -496,26 +520,26 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                /*
                 * construct request
                 */
-               pwd_session->out_buf_len = BN_num_bytes(pwd_session->order) + (2 * BN_num_bytes(pwd_session->prime));
-               if ((pwd_session->out_buf = talloc_array(pwd_session, uint8_t, pwd_session->out_buf_len)) == NULL) {
+               session->out_len = BN_num_bytes(session->order) + (2 * BN_num_bytes(session->prime));
+               if ((session->out = talloc_array(session, uint8_t, session->out_len)) == NULL) {
                        return 0;
                }
-               memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
+               memset(session->out, 0, session->out_len);
 
-               ptr = pwd_session->out_buf;
-               offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(x);
+               ptr = session->out;
+               offset = BN_num_bytes(session->prime) - BN_num_bytes(x);
                BN_bn2bin(x, ptr + offset);
 
-               ptr += BN_num_bytes(pwd_session->prime);
-               offset = BN_num_bytes(pwd_session->prime) - BN_num_bytes(y);
+               ptr += BN_num_bytes(session->prime);
+               offset = BN_num_bytes(session->prime) - BN_num_bytes(y);
                BN_bn2bin(y, ptr + offset);
 
-               ptr += BN_num_bytes(pwd_session->prime);
-               offset = BN_num_bytes(pwd_session->order) - BN_num_bytes(pwd_session->my_scalar);
-               BN_bn2bin(pwd_session->my_scalar, ptr + offset);
+               ptr += BN_num_bytes(session->prime);
+               offset = BN_num_bytes(session->order) - BN_num_bytes(session->my_scalar);
+               BN_bn2bin(session->my_scalar, ptr + offset);
 
-               pwd_session->state = PWD_STATE_COMMIT;
-               ret = send_pwd_request(pwd_session, eap_ds);
+               session->state = PWD_STATE_COMMIT;
+               ret = send_pwd_request(session, eap_ds);
                break;
 
                case PWD_STATE_COMMIT:
@@ -527,7 +551,7 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                /*
                 * process the peer's commit and generate the shared key, k
                 */
-               if (process_peer_commit(pwd_session, buf, inst->bnctx)) {
+               if (process_peer_commit(session, in, in_len, inst->bnctx)) {
                        RDEBUG2("failed to process peer's commit");
                        return 0;
                }
@@ -535,7 +559,7 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                /*
                 * compute our confirm blob
                 */
-               if (compute_server_confirm(pwd_session, pwd_session->my_confirm, inst->bnctx)) {
+               if (compute_server_confirm(session, session->my_confirm, inst->bnctx)) {
                        ERROR("rlm_eap_pwd: failed to compute confirm!");
                        return 0;
                }
@@ -543,32 +567,38 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                /*
                 * construct a response...which is just our confirm blob
                 */
-               pwd_session->out_buf_len = SHA256_DIGEST_LENGTH;
-               if ((pwd_session->out_buf = talloc_array(pwd_session, uint8_t, pwd_session->out_buf_len)) == NULL) {
+               session->out_len = SHA256_DIGEST_LENGTH;
+               if ((session->out = talloc_array(session, uint8_t, session->out_len)) == NULL) {
                        return 0;
                }
 
-               memset(pwd_session->out_buf, 0, pwd_session->out_buf_len);
-               memcpy(pwd_session->out_buf, pwd_session->my_confirm, SHA256_DIGEST_LENGTH);
+               memset(session->out, 0, session->out_len);
+               memcpy(session->out, session->my_confirm, SHA256_DIGEST_LENGTH);
 
-               pwd_session->state = PWD_STATE_CONFIRM;
-               ret = send_pwd_request(pwd_session, eap_ds);
+               session->state = PWD_STATE_CONFIRM;
+               ret = send_pwd_request(session, eap_ds);
                break;
 
        case PWD_STATE_CONFIRM:
+               if (in_len < SHA256_DIGEST_LENGTH) {
+                       RDEBUG("Peer confirm is too short (%zd < %d)",
+                              in_len, SHA256_DIGEST_LENGTH);
+                       return 0;
+               }
+
                if (EAP_PWD_GET_EXCHANGE(hdr) != EAP_PWD_EXCH_CONFIRM) {
                        RDEBUG2("pwd exchange is incorrect: not commit!");
                        return 0;
                }
-               if (compute_peer_confirm(pwd_session, peer_confirm, inst->bnctx)) {
+               if (compute_peer_confirm(session, peer_confirm, inst->bnctx)) {
                        RDEBUG2("pwd exchange cannot compute peer's confirm");
                        return 0;
                }
-               if (CRYPTO_memcmp(peer_confirm, buf, SHA256_DIGEST_LENGTH)) {
+               if (CRYPTO_memcmp(peer_confirm, in, SHA256_DIGEST_LENGTH)) {
                        RDEBUG2("pwd exchange fails: peer confirm is incorrect!");
                        return 0;
                }
-               if (compute_keys(pwd_session, peer_confirm, msk, emsk)) {
+               if (compute_keys(session, peer_confirm, msk, emsk)) {
                        RDEBUG2("pwd exchange cannot generate (E)MSK!");
                        return 0;
                }
@@ -577,7 +607,7 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
                 * return the MSK (in halves)
                 */
                eap_add_reply(handler->request, "MS-MPPE-Recv-Key", msk, MPPE_KEY_LEN);
-               eap_add_reply(handler->request, "MS-MPPE-Send-Key", msk+MPPE_KEY_LEN, MPPE_KEY_LEN);
+               eap_add_reply(handler->request, "MS-MPPE-Send-Key", msk + MPPE_KEY_LEN, MPPE_KEY_LEN);
 
                ret = 1;
                break;
@@ -590,9 +620,9 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
        /*
         * we processed the buffered fragments, get rid of them
         */
-       if (pwd_session->in_buf) {
-               talloc_free(pwd_session->in_buf);
-               pwd_session->in_buf = NULL;
+       if (session->in) {
+               talloc_free(session->in);
+               session->in = NULL;
        }
 
        return ret;
@@ -600,11 +630,10 @@ static int mod_authenticate (void *arg, eap_handler_t *handler)
 
 extern rlm_eap_module_t rlm_eap_pwd;
 rlm_eap_module_t rlm_eap_pwd = {
-       "eap_pwd",
-       eap_pwd_attach,         /* attach */
-       eap_pwd_initiate,       /* initiate to a client */
-       NULL,                   /* no authorization */
-       mod_authenticate,       /* pwd authentication */
-       mod_detach              /* detach */
+       .name           = "eap_pwd",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,             /* Create the initial request */
+       .process        = mod_process,          /* Process next round of EAP method */
+       .detach         = mod_detach            /* Destroy the submodule instance */
 };
 
index b04a57e..189530d 100644 (file)
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
 
-typedef struct eap_pwd_conf {
+typedef struct _eap_pwd_t {
+    BN_CTX *bnctx;
+
     uint32_t   group;
     uint32_t   fragment_size;
     char const *server_id;
     char const *virtual_server;
-} EAP_PWD_CONF;
-
-typedef struct _eap_pwd_t {
-    EAP_PWD_CONF *conf;
-    BN_CTX *bnctx;
 } eap_pwd_t;
 
 #endif  /* _RLM_EAP_PWD_H */
index fb2dfcd..422db30 100644 (file)
@@ -83,33 +83,33 @@ static int eap_sim_sendstart(eap_handler_t *handler)
        words[1] = htons(EAP_SIM_VERSION);
        words[2] = 0;
 
-       newvp = paircreate(packet, PW_EAP_SIM_VERSION_LIST, 0);
-       pairmemcpy(newvp, (uint8_t const *) words, sizeof(words));
+       newvp = fr_pair_afrom_num(packet, PW_EAP_SIM_VERSION_LIST, 0);
+       fr_pair_value_memcpy(newvp, (uint8_t const *) words, sizeof(words));
 
-       pairadd(vps, newvp);
+       fr_pair_add(vps, newvp);
 
        /* set the EAP_ID - new value */
-       newvp = paircreate(packet, PW_EAP_ID, 0);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_ID, 0);
        newvp->vp_integer = ess->sim_id++;
-       pairreplace(vps, newvp);
+       fr_pair_replace(vps, newvp);
 
        /* record it in the ess */
        ess->keys.versionlistlen = 2;
        memcpy(ess->keys.versionlist, words + 1, ess->keys.versionlistlen);
 
        /* the ANY_ID attribute. We do not support re-auth or pseudonym */
-       newvp = paircreate(packet, PW_EAP_SIM_FULLAUTH_ID_REQ, 0);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_SIM_FULLAUTH_ID_REQ, 0);
        newvp->vp_length = 2;
        newvp->vp_octets = p = talloc_array(newvp, uint8_t, 2);
 
        p[0] = 0;
        p[0] = 1;
-       pairadd(vps, newvp);
+       fr_pair_add(vps, newvp);
 
        /* the SUBTYPE, set to start. */
-       newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_SIM_SUBTYPE, 0);
        newvp->vp_integer = EAPSIM_START;
-       pairreplace(vps, newvp);
+       fr_pair_replace(vps, newvp);
 
        return 1;
 }
@@ -124,7 +124,7 @@ static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int id
        /*
         *      Generate a new RAND value, and derive Kc and SRES from Ki
         */
-       ki = pairfind(vps, PW_EAP_SIM_KI, 0, TAG_ANY);
+       ki = fr_pair_find_by_num(vps, PW_EAP_SIM_KI, 0, TAG_ANY);
        if (ki) {
                int i;
 
@@ -132,7 +132,7 @@ static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int id
                 *      Check to see if have a Ki for the IMSI, this allows us to generate the rest
                 *      of the triplets.
                 */
-               algo_version = pairfind(vps, PW_EAP_SIM_ALGO_VERSION, 0, TAG_ANY);
+               algo_version = fr_pair_find_by_num(vps, PW_EAP_SIM_ALGO_VERSION, 0, TAG_ANY);
                if (!algo_version) {
                        REDEBUG("Found Ki, but missing EAP-Sim-Algo-Version");
                        return 0;
@@ -200,10 +200,10 @@ static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int id
         *      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, PW_EAP_SIM_RAND1 + idx, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(vps, PW_EAP_SIM_RAND1 + idx, 0, TAG_ANY);
        /* Hack for backwards compatibility */
        if (!vp) {
-               vp = pairfind(request->reply->vps, PW_EAP_SIM_RAND1 + idx, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, PW_EAP_SIM_RAND1 + idx, 0, TAG_ANY);
        }
        if (!vp) {
                /* bad, we can't find stuff! */
@@ -217,10 +217,10 @@ static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int id
        }
        memcpy(ess->keys.rand[idx], vp->vp_strvalue, EAPSIM_RAND_SIZE);
 
-       vp = pairfind(vps, PW_EAP_SIM_SRES1 + idx, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(vps, PW_EAP_SIM_SRES1 + idx, 0, TAG_ANY);
        /* Hack for backwards compatibility */
        if (!vp) {
-               vp = pairfind(request->reply->vps, PW_EAP_SIM_SRES1 + idx, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, PW_EAP_SIM_SRES1 + idx, 0, TAG_ANY);
        }
        if (!vp) {
                /* bad, we can't find stuff! */
@@ -235,10 +235,10 @@ static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int id
        }
        memcpy(ess->keys.sres[idx], vp->vp_strvalue, EAPSIM_SRES_SIZE);
 
-       vp = pairfind(vps, PW_EAP_SIM_KC1 + idx, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(vps, PW_EAP_SIM_KC1 + idx, 0, TAG_ANY);
        /* Hack for backwards compatibility */
        if (!vp) {
-               vp = pairfind(request->reply->vps, PW_EAP_SIM_KC1 + idx, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, PW_EAP_SIM_KC1 + idx, 0, TAG_ANY);
        }
 
        if (!vp) {
@@ -261,7 +261,7 @@ static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int id
  * Challenges will come from one of three places eventually:
  *
  * 1  from attributes like PW_EAP_SIM_RANDx
- *         (these might be retrived from a database)
+ *         (these might be retrieved from a database)
  *
  * 2  from internally implemented SIM authenticators
  *         (a simple one based upon XOR will be provided)
@@ -305,7 +305,7 @@ static int eap_sim_sendchallenge(eap_handler_t *handler)
        /*
         *      Okay, we got the challenges! Put them into an attribute.
         */
-       newvp = paircreate(packet, PW_EAP_SIM_RAND, 0);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_SIM_RAND, 0);
        newvp->vp_length = 2 + (EAPSIM_RAND_SIZE * 3);
        newvp->vp_octets = p = talloc_array(newvp, uint8_t, newvp->vp_length);
 
@@ -316,14 +316,14 @@ static int eap_sim_sendchallenge(eap_handler_t *handler)
        memcpy(p, ess->keys.rand[1], EAPSIM_RAND_SIZE);
        p += EAPSIM_RAND_SIZE;
        memcpy(p, ess->keys.rand[2], EAPSIM_RAND_SIZE);
-       pairadd(outvps, newvp);
+       fr_pair_add(outvps, newvp);
 
        /*
         *      Set the EAP_ID - new value
         */
-       newvp = paircreate(packet, PW_EAP_ID, 0);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_ID, 0);
        newvp->vp_integer = ess->sim_id++;
-       pairreplace(outvps, newvp);
+       fr_pair_replace(outvps, newvp);
 
        /*
         *      Make a copy of the identity
@@ -334,7 +334,7 @@ static int eap_sim_sendchallenge(eap_handler_t *handler)
        /*
         *      Use the SIM identity, if available
         */
-       newvp = pairfind(*invps, PW_EAP_SIM_IDENTITY, 0, TAG_ANY);
+       newvp = fr_pair_find_by_num(*invps, PW_EAP_SIM_IDENTITY, 0, TAG_ANY);
        if (newvp && newvp->vp_length > 2) {
                uint16_t len;
 
@@ -361,18 +361,18 @@ static int eap_sim_sendchallenge(eap_handler_t *handler)
         *      We store the NONCE_MT in the MAC for the encoder, which
         *      will pull it out before it does the operation.
         */
-       newvp = paircreate(packet, PW_EAP_SIM_MAC, 0);
-       pairmemcpy(newvp, ess->keys.nonce_mt, 16);
-       pairreplace(outvps, newvp);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_SIM_MAC, 0);
+       fr_pair_value_memcpy(newvp, ess->keys.nonce_mt, 16);
+       fr_pair_replace(outvps, newvp);
 
-       newvp = paircreate(packet, PW_EAP_SIM_KEY, 0);
-       pairmemcpy(newvp, ess->keys.K_aut, 16);
-       pairreplace(outvps, newvp);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_SIM_KEY, 0);
+       fr_pair_value_memcpy(newvp, ess->keys.K_aut, 16);
+       fr_pair_replace(outvps, newvp);
 
        /* the SUBTYPE, set to challenge. */
-       newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0);
+       newvp = fr_pair_afrom_num(packet, PW_EAP_SIM_SUBTYPE, 0);
        newvp->vp_integer = EAPSIM_CHALLENGE;
-       pairreplace(outvps, newvp);
+       fr_pair_replace(outvps, newvp);
 
        return 1;
 }
@@ -400,9 +400,9 @@ static int eap_sim_sendsuccess(eap_handler_t *handler)
        ess = (eap_sim_state_t *)handler->opaque;
 
        /* set the EAP_ID - new value */
-       vp = paircreate(packet, PW_EAP_ID, 0);
+       vp = fr_pair_afrom_num(packet, PW_EAP_ID, 0);
        vp->vp_integer = ess->sim_id++;
-       pairreplace(&handler->request->reply->vps, vp);
+       fr_pair_replace(&handler->request->reply->vps, vp);
 
        p = ess->keys.msk;
        eap_add_reply(handler->request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
@@ -459,7 +459,7 @@ static void eap_sim_stateenter(eap_handler_t *handler,
  *     Initiate the EAP-SIM session by starting the state machine
  *      and initiating the state.
  */
-static int eap_sim_initiate(UNUSED void *instance, eap_handler_t *handler)
+static int mod_session_init(UNUSED void *instance, eap_handler_t *handler)
 {
        REQUEST *request = handler->request;
        eap_sim_state_t *ess;
@@ -472,14 +472,14 @@ static int eap_sim_initiate(UNUSED void *instance, eap_handler_t *handler)
        }
 
        handler->opaque = ess;
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        /*
         *      Save the keying material, because it could change on a subsequent retrival.
         */
-       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)) {
+       if (!eap_sim_get_challenge(handler, request->config, 0, ess) ||
+           !eap_sim_get_challenge(handler, request->config, 1, ess) ||
+           !eap_sim_get_challenge(handler, request->config, 2, ess)) {
                return 0;
        }
 
@@ -509,8 +509,8 @@ static int process_eap_sim_start(eap_handler_t *handler, VALUE_PAIR *vps)
        uint16_t simversion;
        ess = (eap_sim_state_t *)handler->opaque;
 
-       nonce_vp = pairfind(vps, PW_EAP_SIM_NONCE_MT, 0, TAG_ANY);
-       selectedversion_vp = pairfind(vps, PW_EAP_SIM_SELECTED_VERSION, 0, TAG_ANY);
+       nonce_vp = fr_pair_find_by_num(vps, PW_EAP_SIM_NONCE_MT, 0, TAG_ANY);
+       selectedversion_vp = fr_pair_find_by_num(vps, PW_EAP_SIM_SELECTED_VERSION, 0, TAG_ANY);
        if (!nonce_vp || !selectedversion_vp) {
                RDEBUG2("Client did not select a version and send a NONCE");
                eap_sim_stateenter(handler, ess, EAPSIM_SERVER_START);
@@ -612,7 +612,7 @@ static int process_eap_sim_challenge(eap_handler_t *handler, VALUE_PAIR *vps)
 /** Authenticate a previously sent challenge
  *
  */
-static int mod_authenticate(UNUSED void *arg, eap_handler_t *handler)
+static int mod_process(UNUSED void *arg, eap_handler_t *handler)
 {
        REQUEST *request = handler->request;
        eap_sim_state_t *ess = handler->opaque;
@@ -637,7 +637,7 @@ static int mod_authenticate(UNUSED void *arg, eap_handler_t *handler)
        /*
         *      See what kind of message we have gotten
         */
-       vp = pairfind(vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY);
        if (!vp) {
                REDEBUG2("No subtype attribute was created, message dropped");
                return 0;
@@ -696,10 +696,7 @@ static int mod_authenticate(UNUSED void *arg, eap_handler_t *handler)
  */
 extern rlm_eap_module_t rlm_eap_sim;
 rlm_eap_module_t rlm_eap_sim = {
-       "eap_sim",
-       NULL,                           /* XXX attach */
-       eap_sim_initiate,               /* Start the initial request */
-       NULL,                           /* XXX authorization */
-       mod_authenticate,               /* authentication */
-       NULL                            /* XXX detach */
+       .name           = "eap_sim",
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process,          /* Process next round of EAP method */
 };
index 7f26c5e..44433c3 100644 (file)
@@ -42,17 +42,15 @@ USES_APPLE_DEPRECATED_API   /* OpenSSL API has been deprecated by Apple */
 
 static CONF_PARSER module_config[] = {
        { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_tls_t, tls_conf_name), NULL },
-
        { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_tls_t, virtual_server), NULL },
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
 /*
  *     Attach the EAP-TLS module.
  */
-static int eaptls_attach(CONF_SECTION *cs, void **instance)
+static int mod_instantiate(CONF_SECTION *cs, void **instance)
 {
        rlm_eap_tls_t           *inst;
 
@@ -80,7 +78,7 @@ static int eaptls_attach(CONF_SECTION *cs, void **instance)
 /*
  *     Send an initial eap-tls request to the peer, using the libeap functions.
  */
-static int eaptls_initiate(void *type_arg, eap_handler_t *handler)
+static int mod_session_init(void *type_arg, eap_handler_t *handler)
 {
        int             status;
        tls_session_t   *ssn;
@@ -90,7 +88,6 @@ static int eaptls_initiate(void *type_arg, eap_handler_t *handler)
        inst = type_arg;
 
        handler->tls = true;
-       handler->finished = false;
 
        /*
         *      EAP-TLS always requires a client certificate.
@@ -112,15 +109,17 @@ static int eaptls_initiate(void *type_arg, eap_handler_t *handler)
         *      related handshaking or application data.
         */
        status = eaptls_start(handler->eap_ds, ssn->peap_flag);
-       RDEBUG2("Start returned %d", status);
-       if (status == 0) {
-               return 0;
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
        }
+       if (status == 0) return 0;
 
        /*
         *      The next stage to process the packet.
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
@@ -128,7 +127,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 CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_process(void *type_arg, eap_handler_t *handler)
 {
        fr_tls_status_t status;
        tls_session_t *tls_session = (tls_session_t *) handler->opaque;
@@ -137,18 +136,21 @@ static int CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *hand
 
        inst = type_arg;
 
-       RDEBUG2("Authenticate");
-
        status = eaptls_process(handler);
-       RDEBUG2("eaptls_process returned %d\n", status);
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       }
+
        switch (status) {
-               /*
-                *      EAP-TLS handshake was successful, return an
-                *      EAP-TLS-Success packet here.
-                *
-                *      If a virtual server was configured, check that
-                *      it accepts the certificates, too.
-                */
+       /*
+        *      EAP-TLS handshake was successful, return an
+        *      EAP-TLS-Success packet here.
+        *
+        *      If a virtual server was configured, check that
+        *      it accepts the certificates, too.
+        */
        case FR_TLS_SUCCESS:
                if (inst->virtual_server) {
                        VALUE_PAIR *vp;
@@ -158,25 +160,25 @@ static int CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *hand
                        fake = request_alloc_fake(request);
                        rad_assert(!fake->packet->vps);
 
-                       fake->packet->vps = paircopy(fake->packet, request->packet->vps);
+                       fake->packet->vps = fr_pair_list_copy(fake->packet, request->packet->vps);
 
                        /* set the virtual server to use */
-                       if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
+                       if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
                                fake->server = vp->vp_strvalue;
                        } else {
                                fake->server = inst->virtual_server;
                        }
 
-                       RDEBUG("Processing EAP-TLS Certificate check:");
+                       RDEBUG2("Validating certificate");
                        rad_virtual_server(fake);
 
                        /* copy the reply vps back to our reply */
-                       pairfilter(request->reply, &request->reply->vps,
+                       fr_pair_list_mcopy_by_num(request->reply, &request->reply->vps,
                                  &fake->reply->vps, 0, 0, TAG_ANY);
 
                        /* reject if virtual server didn't return accept */
                        if (fake->reply->code != PW_CODE_ACCESS_ACCEPT) {
-                               RDEBUG2("Certificates were rejected by the virtual server");
+                               RDEBUG2("Certificate rejected by the virtual server");
                                talloc_free(fake);
                                eaptls_fail(handler, 0);
                                return 0;
@@ -202,7 +204,7 @@ static int CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *hand
        case FR_TLS_OK:
                RDEBUG2("Received unexpected tunneled data after successful handshake");
 #ifndef NDEBUG
-               if ((debug_flag > 2) && fr_log_fp) {
+               if ((rad_debug_lvl > 2) && fr_log_fp) {
                        unsigned int i;
                        unsigned int data_len;
                        unsigned char buffer[1024];
@@ -247,10 +249,8 @@ static int CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *hand
  */
 extern rlm_eap_module_t rlm_eap_tls;
 rlm_eap_module_t rlm_eap_tls = {
-       "eap_tls",
-       eaptls_attach,                  /* attach */
-       eaptls_initiate,                /* Start the initial request */
-       NULL,                           /* authorization */
-       mod_authenticate,               /* authentication */
-       NULL                            /* detach */
+       .name           = "eap_tls",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };
index c4f6886..a1fdbc0 100644 (file)
@@ -65,11 +65,10 @@ typedef struct rlm_eap_tnc {
 
 static CONF_PARSER module_config[] = {
        { "connection_string", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, 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 */
+       CONF_PARSER_TERMINATOR
 };
 
-static int tnc_attach(CONF_SECTION *cs, void **instance)
+static int mod_instantiate(CONF_SECTION *cs, void **instance)
 {
        rlm_eap_tnc_t *inst;
        TNC_Result result;
@@ -131,7 +130,7 @@ static int mod_detach(void *instance)
  * For this package, only 'Identifier' has to be set dynamically. Any
  * other information is static.
  */
-static int tnc_initiate(void *instance, eap_handler_t *handler)
+static int mod_session_init(void *instance, eap_handler_t *handler)
 {
        rlm_eap_tnc_t *inst = instance;
        REQUEST *request = NULL;
@@ -189,7 +188,7 @@ static int tnc_initiate(void *instance, eap_handler_t *handler)
         *
         *      Something has gone very wrong if the User-Name doesn't exist.
         */
-       username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+       username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
 
        RDEBUG("Username for TNC connection: %s", username->vp_strvalue);
 
@@ -237,7 +236,7 @@ static int tnc_initiate(void *instance, eap_handler_t *handler)
         *      stored in 'handler->eap_ds', which will be given back
         *      to us...
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
@@ -252,7 +251,7 @@ static int tnc_initiate(void *instance, eap_handler_t *handler)
  * @param handler The eap_handler_t.
  * @return True, if successfully, else false.
  */
-static int mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
+static int mod_process(UNUSED void *instance, eap_handler_t *handler)
 {
        TNC_ConnectionID conn_id;
        TNC_Result result;
@@ -305,17 +304,17 @@ static int mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
 
        case TNC_CONNECTION_STATE_ACCESS_NONE:
                code = PW_EAP_FAILURE;
-               pairmake_config("TNC-Status", "None", T_OP_SET);
+               pair_make_config("TNC-Status", "None", T_OP_SET);
                break;
 
        case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
                code = PW_EAP_SUCCESS;
-               pairmake_config("TNC-Status", "Access", T_OP_SET);
+               pair_make_config("TNC-Status", "Access", T_OP_SET);
                break;
 
        case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
                code = PW_EAP_SUCCESS;
-               pairmake_config("TNC-Status", "Isolate", T_OP_SET);
+               pair_make_config("TNC-Status", "Isolate", T_OP_SET);
                break;
 
        default:
@@ -350,10 +349,9 @@ static int mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
  */
 extern rlm_eap_module_t rlm_eap_tnc;
 rlm_eap_module_t rlm_eap_tnc = {
-               "eap_tnc",
-               tnc_attach,             /* attach */
-               tnc_initiate,           /* Start the initial request */
-               NULL,                   /* authorization */
-               mod_authenticate,       /* authentication */
-               mod_detach              /* detach */
+       .name           = "eap_tnc",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process,          /* Process next round of EAP method */
+       .detach         = mod_detach            /* detach */
 };
index 5fa35dc..c00f7a2 100644 (file)
@@ -82,15 +82,14 @@ static CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 
 /*
  *     Attach the module.
  */
-static int eapttls_attach(CONF_SECTION *cs, void **instance)
+static int mod_instantiate(CONF_SECTION *cs, void **instance)
 {
        rlm_eap_ttls_t          *inst;
 
@@ -149,7 +148,7 @@ static ttls_tunnel_t *ttls_alloc(TALLOC_CTX *ctx, rlm_eap_ttls_t *inst)
 /*
  *     Send an initial eap-tls request to the peer, using the libeap functions.
  */
-static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
+static int mod_session_init(void *type_arg, eap_handler_t *handler)
 {
        int             status;
        tls_session_t   *ssn;
@@ -161,7 +160,6 @@ static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
        inst = type_arg;
 
        handler->tls = true;
-       handler->finished = false;
 
        /*
         *      Check if we need a client certificate.
@@ -171,7 +169,7 @@ static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
         * EAP-TLS-Require-Client-Cert attribute will override
         * the require_client_cert configuration option.
         */
-       vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
        if (vp) {
                client_cert = vp->vp_integer ? true : false;
        } else {
@@ -195,15 +193,17 @@ static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
         *      related handshaking or application data.
         */
        status = eaptls_start(handler->eap_ds, ssn->peap_flag);
-       RDEBUG2("Start returned %d", status);
-       if (status == 0) {
-               return 0;
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
        }
+       if (status == 0) return 0;
 
        /*
         *      The next stage to process the packet.
         */
-       handler->stage = AUTHENTICATE;
+       handler->stage = PROCESS;
 
        return 1;
 }
@@ -212,7 +212,7 @@ static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
 /*
  *     Do authentication, by letting EAP-TLS do most of the work.
  */
-static int mod_authenticate(void *arg, eap_handler_t *handler)
+static int mod_process(void *arg, eap_handler_t *handler)
 {
        int rcode;
        fr_tls_status_t status;
@@ -229,7 +229,12 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
         *      Process TLS layer until done.
         */
        status = eaptls_process(handler);
-       RDEBUG2("eaptls_process returned %d\n", status);
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       }
+
        switch (status) {
        /*
         *      EAP-TLS handshake was successful, tell the
@@ -248,7 +253,7 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
                        if (t->accept_vps) {
                                RDEBUG2("Using saved attributes from the original Access-Accept");
                                rdebug_pair_list(L_DBG_LVL_2, request, t->accept_vps, NULL);
-                               pairfilter(handler->request->reply,
+                               fr_pair_list_mcopy_by_num(handler->request->reply,
                                           &handler->request->reply->vps,
                                           &t->accept_vps, 0, 0, TAG_ANY);
                        } else if (t->use_tunneled_reply) {
@@ -352,10 +357,8 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
  */
 extern rlm_eap_module_t rlm_eap_ttls;
 rlm_eap_module_t rlm_eap_ttls = {
-       "eap_ttls",
-       eapttls_attach,                 /* attach */
-       eapttls_initiate,               /* Start the initial request */
-       NULL,                           /* authorization */
-       mod_authenticate,               /* authentication */
-       NULL                            /* detach */
+       .name           = "eap_ttls",
+       .instantiate    = mod_instantiate,      /* Create new submodule instance */
+       .session_init   = mod_session_init,     /* Initialise a new EAP session */
+       .process        = mod_process           /* Process next round of EAP method */
 };
index 9708b87..63d33a8 100644 (file)
@@ -144,7 +144,6 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
        size_t          offset;
        size_t          size;
        size_t          data_left = data_len;
-       char            *p;
        VALUE_PAIR      *first = NULL;
        VALUE_PAIR      *vp;
        RADIUS_PACKET   *packet = fake->packet; /* FIXME: api issues */
@@ -245,7 +244,7 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
 
                        if ((size_t) decoded != size + 2) {
                                REDEBUG2("diameter2vp failed to entirely decode VSA");
-                               pairfree(&vp);
+                               fr_pair_list_free(&vp);
                                goto do_octets;
                        }
 
@@ -258,10 +257,10 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                 *      Create it.  If this fails, it's because we're OOM.
                 */
        do_octets:
-               vp = paircreate(packet, attr, vendor);
+               vp = fr_pair_afrom_num(packet, attr, vendor);
                if (!vp) {
                        RDEBUG2("Failure in creating VP");
-                       pairfree(&first);
+                       fr_pair_list_free(&first);
                        return NULL;
                }
 
@@ -282,12 +281,12 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                                 *      attribute.
                                 */
                raw:
-                               if (vp) pairfree(&vp);
+                               if (vp) fr_pair_list_free(&vp);
                                da = dict_unknown_afrom_fields(packet, attr, vendor);
                                if (!da) return NULL;
-                               vp = pairalloc(packet, da);
+                               vp = fr_pair_afrom_da(packet, da);
                                if (!vp) return NULL;
-                               pairmemcpy(vp, data, size);
+                               fr_pair_value_memcpy(vp, data, size);
                                break;
                        }
                        memcpy(&vp->vp_integer, data, vp->vp_length);
@@ -312,8 +311,8 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                        if (size != vp->vp_length) {
                                RDEBUG2("Invalid length attribute %d",
                                       attr);
-                               pairfree(&first);
-                               pairfree(&vp);
+                               fr_pair_list_free(&first);
+                               fr_pair_list_free(&vp);
                                return NULL;
                        }
                        memcpy(&vp->vp_ipaddr, data, vp->vp_length);
@@ -349,15 +348,9 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                        memcpy(vp->vp_ipv6prefix, data, vp->vp_length);
                        break;
 
-                       /*
-                        *      Ensure it's NUL terminated.
-                        */
                case PW_TYPE_STRING:
-                       vp->vp_strvalue = p = talloc_array(vp, char, size + 1);
-                       vp->type = VT_DATA;
-                       memcpy(p, data, size);
-                       p[size] = '\0';
-                       vp->vp_length = strlen(p);
+                       fr_pair_value_bstrncpy(vp, data, size);
+                       vp->vp_length = strlen(vp->vp_strvalue); /* embedded zeros are NOT allowed */
                        break;
 
                        /*
@@ -365,7 +358,7 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                         */
                case PW_TYPE_OCTETS:
                default:
-                       pairmemcpy(vp, data, size);
+                       fr_pair_value_memcpy(vp, data, size);
                        break;
                }
 
@@ -398,8 +391,8 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                        if ((vp->vp_length < 8) ||
                            (vp->vp_length > 16)) {
                                RDEBUG("Tunneled challenge has invalid length");
-                               pairfree(&first);
-                               pairfree(&vp);
+                               fr_pair_list_free(&first);
+                               fr_pair_list_free(&vp);
                                return NULL;
                        }
 
@@ -409,8 +402,8 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                        if (memcmp(challenge, vp->vp_octets,
                                   vp->vp_length) != 0) {
                                RDEBUG("Tunneled challenge is incorrect");
-                               pairfree(&first);
-                               pairfree(&vp);
+                               fr_pair_list_free(&first);
+                               fr_pair_list_free(&vp);
                                return NULL;
                        }
                }
@@ -583,7 +576,7 @@ static int vp2diameter(REQUEST *request, tls_session_t *tls_session, VALUE_PAIR
 #ifndef NDEBUG
                size_t i;
 
-               if ((debug_flag > 2) && fr_log_fp) {
+               if ((rad_debug_lvl > 2) && fr_log_fp) {
                        for (i = 0; i < total; i++) {
                                if ((i & 0x0f) == 0) fprintf(fr_log_fp, "  TTLS tunnel data out %04x: ", (int) i);
 
@@ -649,6 +642,16 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                rcode = RLM_MODULE_OK;
 
                /*
+                *      Always delete MPPE keys & encryption policy
+                *      from the tunneled reply.  These never get sent
+                *      back to the user.
+                */
+               fr_pair_delete_by_num(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_delete_by_num(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_delete_by_num(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_delete_by_num(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
+
+               /*
                 *      MS-CHAP2-Success means that we do NOT return
                 *      an Access-Accept, but instead tunnel that
                 *      attribute to the client, and keep going with
@@ -657,27 +660,18 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 *      packet, and we will send EAP-Success.
                 */
                vp = NULL;
-               pairfilter(tls_session, &vp, &reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_list_mcopy_by_num(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");
                        rcode = RLM_MODULE_HANDLED;
                        t->authenticated = true;
 
                        /*
-                        *      Delete MPPE keys & encryption policy.  We don't
-                        *      want these here.
-                        */
-                       pairdelete(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY);
-                       pairdelete(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY);
-                       pairdelete(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
-                       pairdelete(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
-
-                       /*
                         *      Use the tunneled reply, but not now.
                         */
                        if (t->use_tunneled_reply) {
                                rad_assert(!t->accept_vps);
-                               pairfilter(t, &t->accept_vps, &reply->vps,
+                               fr_pair_list_mcopy_by_num(t, &t->accept_vps, &reply->vps,
                                          0, 0, TAG_ANY);
                                rad_assert(!reply->vps);
                        }
@@ -685,27 +679,27 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                } else { /* no MS-CHAP2-Success */
                        /*
                         *      Can only have EAP-Message if there's
-                        *      no MS-CHAP2-Success.  (FIXME: EAP-MSCHAP?)
+                        *      no MS-CHAP2-Success.
                         *
                         *      We also do NOT tunnel the EAP-Success
                         *      attribute back to the client, as the client
                         *      can figure it out, from the non-tunneled
                         *      EAP-Success packet.
                         */
-                       pairfilter(tls_session, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
-                       pairfree(&vp);
+                       fr_pair_list_mcopy_by_num(tls_session, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+                       fr_pair_list_free(&vp);
                }
 
                /* move channel binding responses; we need to send them */
-               pairfilter(tls_session, &vp, &reply->vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY);
-               if (pairfind(vp, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY) != NULL) {
+               fr_pair_list_mcopy_by_num(tls_session, &vp, &reply->vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY);
+               if (fr_pair_find_by_num(vp, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY) != NULL) {
                        t->authenticated = true;
                        /*
                         *      Use the tunneled reply, but not now.
                         */
                        if (t->use_tunneled_reply) {
                                rad_assert(!t->accept_vps);
-                               pairfilter(t, &t->accept_vps, &reply->vps,
+                               fr_pair_list_mcopy_by_num(t, &t->accept_vps, &reply->vps,
                                          0, 0, TAG_ANY);
                                rad_assert(!reply->vps);
                        }
@@ -721,7 +715,7 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                        rdebug_pair_list(L_DBG_LVL_1, request, vp, NULL);
 
                        vp2diameter(request, tls_session, vp);
-                       pairfree(&vp);
+                       fr_pair_list_free(&vp);
                }
 
                /*
@@ -732,8 +726,8 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 *      tunneled user!
                 */
                if (t->use_tunneled_reply) {
-                       pairdelete(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
-                       pairfilter(request->reply, &request->reply->vps,
+                       fr_pair_delete_by_num(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+                       fr_pair_list_mcopy_by_num(request->reply, &request->reply->vps,
                                  &reply->vps, 0, 0, TAG_ANY);
                }
                break;
@@ -758,8 +752,8 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 *
                 *      Get rid of the old State, too.
                 */
-               pairfree(&t->state);
-               pairfilter(t, &t->state, &reply->vps, PW_STATE, 0, TAG_ANY);
+               fr_pair_list_free(&t->state);
+               fr_pair_list_mcopy_by_num(t, &t->state, &reply->vps, PW_STATE, 0, TAG_ANY);
 
                /*
                 *      We should really be a bit smarter about this,
@@ -769,7 +763,7 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 *      method works in 99.9% of the situations.
                 */
                vp = NULL;
-               pairfilter(t, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+               fr_pair_list_mcopy_by_num(t, &vp, &reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
 
                /*
                 *      There MUST be a Reply-Message in the challenge,
@@ -779,10 +773,10 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 *      we MUST create one, with an empty string as
                 *      it's value.
                 */
-               pairfilter(t, &vp, &reply->vps, PW_REPLY_MESSAGE, 0, TAG_ANY);
+               fr_pair_list_mcopy_by_num(t, &vp, &reply->vps, PW_REPLY_MESSAGE, 0, TAG_ANY);
 
                /* also move chbind messages, if any */
-               pairfilter(t, &vp, &reply->vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA,
+               fr_pair_list_mcopy_by_num(t, &vp, &reply->vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA,
                          TAG_ANY);
 
                /*
@@ -791,7 +785,7 @@ static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_se
                 */
                if (vp) {
                        vp2diameter(request, tls_session, vp);
-                       pairfree(&vp);
+                       fr_pair_list_free(&vp);
                }
                rcode = RLM_MODULE_HANDLED;
                break;
@@ -842,7 +836,7 @@ static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data
                fake->reply = talloc_steal(fake, request->proxy_reply);
                request->proxy_reply = NULL;
 
-               if ((debug_flag > 0) && fr_log_fp) {
+               if ((rad_debug_lvl > 0) && fr_log_fp) {
                        fprintf(fr_log_fp, "server %s {\n",
                                (!fake->server) ? "" : fake->server);
                }
@@ -855,7 +849,7 @@ static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data
                rcode = rad_postauth(fake);
                RDEBUG2("post-auth returns %d", rcode);
 
-               if ((debug_flag > 0) && fr_log_fp) {
+               if ((rad_debug_lvl > 0) && fr_log_fp) {
                        fprintf(fr_log_fp, "} # server %s\n",
                                (!fake->server) ? "" : fake->server);
 
@@ -899,7 +893,7 @@ static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data
         *      the basis for the reply to the NAS.  We don't want that,
         *      so we toss it, after we've had our way with it.
         */
-       pairfree(&handler->request->proxy_reply->vps);
+       fr_pair_list_free(&handler->request->proxy_reply->vps);
 
        switch (rcode) {
        case RLM_MODULE_REJECT:
@@ -975,7 +969,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        }
 
 #ifndef NDEBUG
-       if ((debug_flag > 2) && fr_log_fp) {
+       if ((rad_debug_lvl > 2) && fr_log_fp) {
                size_t i;
 
                for (i = 0; i < data_len; i++) {
@@ -994,7 +988,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        }
 
        /*
-        *      Allocate a fake REQUEST structe.
+        *      Allocate a fake REQUEST structure.
         */
        fake = request_alloc_fake(request);
 
@@ -1012,7 +1006,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        /*
         *      Tell the request that it's a fake one.
         */
-       pairmake_packet("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
+       pair_make_request("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
 
        RDEBUG("Got tunneled request");
        rdebug_pair_list(L_DBG_LVL_1, request, fake->packet->vps, NULL);
@@ -1020,8 +1014,8 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        /*
         *      Update other items in the REQUEST data structure.
         */
-       fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
-       fake->password = pairfind(fake->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+       fake->username = fr_pair_find_by_num(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+       fake->password = fr_pair_find_by_num(fake->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
 
        /*
         *      No User-Name, try to create one from stored data.
@@ -1032,25 +1026,19 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                 *      an EAP-Identity, and pull it out of there.
                 */
                if (!t->username) {
-                       vp = pairfind(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
+                       vp = fr_pair_find_by_num(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
                        if (vp &&
                            (vp->vp_length >= EAP_HEADER_LEN + 2) &&
                            (vp->vp_strvalue[0] == PW_EAP_RESPONSE) &&
                            (vp->vp_strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) &&
                            (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) {
-                               char *p;
-
                                /*
                                 *      Create & remember a User-Name
                                 */
-                               t->username = pairmake(t, NULL, "User-Name", NULL, T_OP_EQ);
+                               t->username = fr_pair_make(t, NULL, "User-Name", NULL, T_OP_EQ);
                                rad_assert(t->username != NULL);
-                               t->username->vp_length = vp->vp_length - 5;
 
-                               t->username->vp_strvalue = p = talloc_array(t->username, char,
-                                                                           t->username->vp_length + 1);
-                               memcpy(p, vp->vp_octets + 5, t->username->vp_length);
-                               p[t->username->vp_length] = 0;
+                               fr_pair_value_bstrncpy(t->username, vp->vp_octets + 5, vp->vp_length - 5);
 
                                RDEBUG("Got tunneled identity of %s",
                                       t->username->vp_strvalue);
@@ -1061,10 +1049,10 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                                 */
                                if (t->default_method != 0) {
                                        RDEBUG("Setting default EAP type for tunneled EAP session");
-                                       vp = paircreate(fake, PW_EAP_TYPE, 0);
+                                       vp = fr_pair_afrom_num(fake, PW_EAP_TYPE, 0);
                                        rad_assert(vp != NULL);
                                        vp->vp_integer = t->default_method;
-                                       pairadd(&fake->config_items, vp);
+                                       fr_pair_add(&fake->config, vp);
                                }
 
                        } else {
@@ -1078,9 +1066,9 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                } /* else there WAS a t->username */
 
                if (t->username) {
-                       vp = paircopy(fake->packet, t->username);
-                       pairadd(&fake->packet->vps, vp);
-                       fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+                       vp = fr_pair_list_copy(fake->packet, t->username);
+                       fr_pair_add(&fake->packet->vps, vp);
+                       fake->username = fr_pair_find_by_num(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                }
        } /* else the request ALREADY had a User-Name */
 
@@ -1088,8 +1076,8 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
         *      Add the State attribute, too, if it exists.
         */
        if (t->state) {
-               vp = paircopy(fake->packet, t->state);
-               if (vp) pairadd(&fake->packet->vps, vp);
+               vp = fr_pair_list_copy(fake->packet, t->state);
+               if (vp) fr_pair_add(&fake->packet->vps, vp);
        }
 
        /*
@@ -1122,7 +1110,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      AND attributes which are copied there
                         *      from below.
                         */
-                       if (pair_find_by_da(fake->packet->vps, vp->da, TAG_ANY)) {
+                       if (fr_pair_find_by_da(fake->packet->vps, vp->da, TAG_ANY)) {
                                continue;
                        }
 
@@ -1156,12 +1144,12 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      Don't copy from the head, we've already
                         *      checked it.
                         */
-                       copy = paircopy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
-                       pairadd(&fake->packet->vps, copy);
+                       copy = fr_pair_list_copy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY);
+                       fr_pair_add(&fake->packet->vps, copy);
                }
        }
 
-       if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) {
                fake->server = vp->vp_strvalue;
 
        } else if (t->virtual_server) {
@@ -1170,7 +1158,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        } /* else fake->server == request->server */
 
 
-       if ((debug_flag > 0) && fr_log_fp) {
+       if ((rad_debug_lvl > 0) && fr_log_fp) {
                RDEBUG("Sending tunneled request");
        }
 
@@ -1194,7 +1182,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                /* encapsulate response here */
                if (req->response) {
                        RDEBUG("sending chbind response");
-                       pairadd(&fake->reply->vps,
+                       fr_pair_add(&fake->reply->vps,
                                eap_chbind_packet2vp(fake, req->response));
                } else {
                        RDEBUG("no chbind response");
@@ -1220,7 +1208,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        switch (fake->reply->code) {
        case 0:                 /* No reply code, must be proxied... */
 #ifdef WITH_PROXY
-               vp = pairfind(fake->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(fake->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
                if (vp) {
                        eap_tunnel_data_t *tunnel;
                        RDEBUG("Tunneled authentication will be proxied to %s", vp->vp_strvalue);
@@ -1229,8 +1217,8 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      Tell the original request that it's going
                         *      to be proxied.
                         */
-                       pairfilter(request, &request->config_items,
-                                 &fake->config_items,
+                       fr_pair_list_mcopy_by_num(request, &request->config,
+                                 &fake->config,
                                  PW_PROXY_TO_REALM, 0, TAG_ANY);
 
                        /*
index 283fc77..ed94bba 100644 (file)
@@ -50,8 +50,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static int rlm_example_cmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
@@ -70,10 +69,6 @@ static int rlm_example_cmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE
  *     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)
 {
@@ -114,7 +109,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
        /*
         *  Look for the 'state' attribute.
         */
-       state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
+       state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
        if (state != NULL) {
                RDEBUG("Found reply to access challenge");
                return RLM_MODULE_OK;
@@ -123,8 +118,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
        /*
         *  Create the challenge, and add it to the reply.
         */
-       pairmake_reply("Reply-Message", "This is a challenge", T_OP_EQ);
-       pairmake_reply("State", "0", T_OP_EQ);
+       pair_make_reply("Reply-Message", "This is a challenge", T_OP_EQ);
+       pair_make_reply("State", "0", T_OP_EQ);
 
        /*
         *  Mark the packet as an Access-Challenge packet.
@@ -202,25 +197,20 @@ static int mod_detach(UNUSED void *instance)
  */
 extern module_t rlm_example;
 module_t rlm_example = {
-       RLM_MODULE_INIT,
-       "example",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_example_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "example",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_example_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
 #ifdef WITH_ACCOUNTING
-               mod_preacct,            /* preaccounting */
-               mod_accounting,         /* accounting */
-               mod_checksimul,         /* checksimul */
-#else
-               NULL, NULL, NULL,
+               [MOD_PREACCT]           = mod_preacct,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_SESSION]           = mod_checksimul
 #endif
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
        },
 };
index b129d16..465bfb1 100644 (file)
@@ -63,8 +63,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static char const special[] = "\\'\"`<>|; \t\r\n()[]?#$^&*=";
@@ -180,7 +179,7 @@ static ssize_t exec_xlat(void *instance, REQUEST *request, char const *fmt, char
         *      This function does it's own xlat of the input program
         *      to execute.
         */
-       result = radius_exec_program(out, outlen, NULL, request, fmt,  input_pairs ? *input_pairs : NULL,
+       result = radius_exec_program(request, out, outlen, NULL, request, fmt,  input_pairs ? *input_pairs : NULL,
                                     inst->wait, inst->shell_escape, inst->timeout);
        if (result != 0) {
                out[0] = '\0';
@@ -204,7 +203,7 @@ static ssize_t exec_xlat(void *instance, REQUEST *request, char const *fmt, char
  *     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)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        char const *p;
        rlm_exec_t      *inst = instance;
@@ -340,7 +339,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_exec_dispatch(void *instance, REQUEST *r
         *      This function does it's own xlat of the input program
         *      to execute.
         */
-       status = radius_exec_program(out, sizeof(out), inst->output ? &answer : NULL, request,
+       status = radius_exec_program(request, out, sizeof(out), inst->output ? &answer : NULL, request,
                                     inst->program, inst->input ? *input_pairs : NULL,
                                     inst->wait, inst->shell_escape, inst->timeout);
        rcode = rlm_exec_status2rcode(request, out, strlen(out), status);
@@ -351,9 +350,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_exec_dispatch(void *instance, REQUEST *r
         *      If we're not waiting, then there are no output pairs.
         */
        if (inst->output) {
-               pairmove(request, output_pairs, &answer);
+               fr_pair_list_move(request, output_pairs, &answer);
        }
-       pairfree(&answer);
+       fr_pair_list_free(&answer);
 
        return rcode;
 }
@@ -374,10 +373,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        bool            we_wait = false;
        VALUE_PAIR      *vp, *tmp;
 
-       vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY);
        if (vp) {
                we_wait = false;
-       } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) {
+       } else if ((vp = fr_pair_find_by_num(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) {
                we_wait = true;
        }
        if (!vp) {
@@ -390,15 +389,15 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        }
 
        tmp = NULL;
-       status = radius_exec_program(out, sizeof(out), &tmp, request, vp->vp_strvalue, request->packet->vps,
+       status = radius_exec_program(request, out, sizeof(out), &tmp, request, vp->vp_strvalue, request->packet->vps,
                                     we_wait, inst->shell_escape, inst->timeout);
        rcode = rlm_exec_status2rcode(request, out, strlen(out), status);
 
        /*
         *      Always add the value-pairs to the reply.
         */
-       pairmove(request->reply, &request->reply->vps, &tmp);
-       pairfree(&tmp);
+       fr_pair_list_move(request->reply, &request->reply->vps, &tmp);
+       fr_pair_list_free(&tmp);
 
        finish:
        switch (rcode) {
@@ -437,17 +436,17 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
                return mod_exec_dispatch(instance, request);
        }
 
-       vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY);
        if (vp) {
                we_wait = true;
-       } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) {
+       } else if ((vp = fr_pair_find_by_num(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) {
                we_wait = false;
        }
        if (!vp) {
                return RLM_MODULE_NOOP;
        }
 
-       status = radius_exec_program(out, sizeof(out), NULL, request, vp->vp_strvalue, request->packet->vps,
+       status = radius_exec_program(request, out, sizeof(out), NULL, request, vp->vp_strvalue, request->packet->vps,
                                     we_wait, inst->shell_escape, inst->timeout);
        return rlm_exec_status2rcode(request, out, strlen(out), status);
 }
@@ -463,25 +462,23 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
  */
 extern module_t rlm_exec;
 module_t rlm_exec = {
-       RLM_MODULE_INIT,
-       "exec",                         /* Name */
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_exec_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_exec_dispatch,      /* authentication */
-               mod_exec_dispatch,      /* authorization */
-               mod_exec_dispatch,      /* pre-accounting */
-               mod_accounting,         /* accounting */
-               NULL,                   /* check simul */
-               mod_exec_dispatch,      /* pre-proxy */
-               mod_exec_dispatch,      /* post-proxy */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "exec",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_exec_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_exec_dispatch,
+               [MOD_AUTHORIZE]         = mod_exec_dispatch,
+               [MOD_PREACCT]           = mod_exec_dispatch,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_PRE_PROXY]         = mod_exec_dispatch,
+               [MOD_POST_PROXY]        = mod_exec_dispatch,
+               [MOD_POST_AUTH]         = mod_post_auth,
 #ifdef WITH_COA
-               , mod_exec_dispatch,
-               mod_exec_dispatch
+               [MOD_RECV_COA]          = mod_exec_dispatch,
+               [MOD_SEND_COA]          = mod_exec_dispatch
 #endif
        },
 };
index cd67a55..0768405 100644 (file)
@@ -37,7 +37,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
        VALUE_PAIR *vp, *check_item;
        char date[50];
 
-       check_item = pairfind(request->config_items, PW_EXPIRATION, 0, TAG_ANY);
+       check_item = fr_pair_find_by_num(request->config, PW_EXPIRATION, 0, TAG_ANY);
        if (!check_item) return RLM_MODULE_NOOP;
 
        /*
@@ -63,9 +63,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
         *      Else the account hasn't expired, but it may do so
         *      in the future.  Set Session-Timeout.
         */
-       vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
        if (!vp) {
-               vp = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
+               vp = radius_pair_create(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);
@@ -120,21 +120,12 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
  */
 extern module_t rlm_expiration;
 module_t rlm_expiration = {
-       RLM_MODULE_INIT,
-       "expiration",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       0,
-       NULL,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_authorize           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "expiration",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_POST_AUTH]         = mod_authorize
        },
 };
index 3871595..5d6f626 100644 (file)
@@ -104,24 +104,24 @@ static int presufcmp(UNUSED void *instance,
        /*
         *      If Strip-User-Name == No, then don't do any more.
         */
-       vp = pairfind(check_pairs, PW_STRIP_USER_NAME, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(check_pairs, PW_STRIP_USER_NAME, 0, TAG_ANY);
        if (vp && !vp->vp_integer) return ret;
 
        /*
         *      See where to put the stripped user name.
         */
-       vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(check_pairs, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
        if (!vp) {
                /*
                 *      If "request" is NULL, then the memory will be
                 *      lost!
                 */
-               vp = radius_paircreate(req->packet, &request, PW_STRIPPED_USER_NAME, 0);
+               vp = radius_pair_create(req->packet, &request, PW_STRIPPED_USER_NAME, 0);
                if (!vp) return ret;
                req->username = vp;
        }
 
-       pairstrcpy(vp, rest);
+       fr_pair_value_strcpy(vp, rest);
 
        return ret;
 }
@@ -183,13 +183,13 @@ static int genericcmp(UNUSED void *instance,
                if (radius_xlat(value, sizeof(value), request, name, NULL, NULL) < 0) {
                        return 0;
                }
-               vp = pairmake(req, NULL, check->da->name, value, check->op);
+               vp = fr_pair_make(req, NULL, check->da->name, value, check->op);
 
                /*
                 *      Paircmp returns 0 for failed comparison,
                 *      1 for succeeded.
                 */
-               rcode = paircmp(check, vp);
+               rcode = fr_pair_cmp(check, vp);
 
                /*
                 *      We're being called from radius_callback_compare,
@@ -209,7 +209,7 @@ static int genericcmp(UNUSED void *instance,
                 *      returns 0 for matched, and 1 for didn't match.
                 */
                rcode = !rcode;
-               pairfree(&vp);
+               fr_pair_list_free(&vp);
 
                return rcode;
        }
index d1582a8..a7f163b 100644 (file)
@@ -50,7 +50,7 @@ typedef struct rlm_expr_t {
 
 static const CONF_PARSER module_config[] = {
        { "safe_characters", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_expr_t, allowed_chars), "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -224,54 +224,48 @@ static bool get_number(REQUEST *request, char const **string, int64_t *answer)
         *      Look for an attribute.
         */
        if (*p == '&') {
-               ssize_t slen;
-               VALUE_PAIR *vp;
-               value_pair_tmpl_t vpt;
+               ssize_t         slen;
+               VALUE_PAIR      *vp;
+               vp_tmpl_t       vpt;
 
                p += 1;
 
                slen = tmpl_from_attr_substr(&vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
-               if (slen < 0) {
-                       RDEBUG("Failed parsing attribute name '%s': %s", p, fr_strerror());
+               if (slen <= 0) {
+                       REDEBUG("Failed parsing attribute name '%s': %s", p, fr_strerror());
                        return false;
                }
 
                p += slen;
 
                if (tmpl_find_vp(&vp, request, &vpt) < 0) {
-                       RDEBUG("Can't find &%s", vpt.tmpl_da->name);
+                       RWDEBUG("Can't find &%.*s.  Using 0 as operand value", (int)vpt.len, vpt.name);
                        x = 0;
                        goto done;
                }
 
-               switch (vp->da->type) {
-               default:
-                       RDEBUG("WARNING: Non-integer attribute %s", vp->da->name);
-                       x = 0;
-                       break;
+               if (vp->da->type != PW_TYPE_INTEGER64) {
+                       value_data_t    value;
 
-               case PW_TYPE_INTEGER64:
-                       /*
-                        *      FIXME: error out if the number is too large.
-                        */
-                       x = vp->vp_integer64;
-                       break;
-
-               case PW_TYPE_INTEGER:
-                       x = vp->vp_integer;
-                       break;
-
-               case PW_TYPE_SIGNED:
-                       x = vp->vp_signed;
-                       break;
-
-               case PW_TYPE_SHORT:
-                       x = vp->vp_short;
-                       break;
+                       if (value_data_cast(vp, &value, PW_TYPE_INTEGER64, NULL, vp->da->type, vp->da, &vp->data, vp->vp_length) < 0) {
+                               REDEBUG("Failed converting &%.*s to an integer value: %s", (int) vpt.len,
+                                       vpt.name, fr_strerror());
+                               return false;
+                       }
+                       if (value.integer64 > INT64_MAX) {
+                       overflow:
+                               REDEBUG("Value of &%.*s (%"PRIu64 ") would overflow a signed 64bit integer "
+                                       "(our internal arithmetic type)", (int)vpt.len, vpt.name, value.integer64);
+                               return false;
+                       }
+                       x = (int64_t)value.integer64;
 
-               case PW_TYPE_BYTE:
-                       x = vp->vp_byte;
-                       break;
+                       RINDENT();
+                       RDEBUG3("&%.*s --> %" PRIu64, (int)vpt.len, vpt.name, x);
+                       REXDENT();
+               } else {
+                       if (vp->vp_integer64 > INT64_MAX) goto overflow;
+                       x = (int64_t)vp->vp_integer64;
                }
 
                goto done;
@@ -976,7 +970,7 @@ static ssize_t sha1_xlat(UNUSED void *instance, REQUEST *request,
        uint8_t digest[20];
        ssize_t i, len, inlen;
        uint8_t const *p;
-       fr_SHA1_CTX ctx;
+       fr_sha1_ctx ctx;
 
        /*
         *      We need room for at least one octet of output.
@@ -1168,7 +1162,7 @@ static ssize_t hmac_sha1_xlat(UNUSED void *instance, REQUEST *request,
 static ssize_t pairs_xlat(UNUSED void *instance, REQUEST *request,
                          char const *fmt, char *out, size_t outlen)
 {
-       value_pair_tmpl_t vpt;
+       vp_tmpl_t vpt;
        vp_cursor_t cursor;
        size_t len, freespace = outlen;
        char *p = out;
@@ -1282,7 +1276,7 @@ static ssize_t base64_to_hex_xlat(UNUSED void *instance, REQUEST *request,
 static ssize_t explode_xlat(UNUSED void *instance, REQUEST *request,
                            char const *fmt, char *out, size_t outlen)
 {
-       value_pair_tmpl_t vpt;
+       vp_tmpl_t vpt;
        vp_cursor_t cursor, to_merge;
        VALUE_PAIR *vp, *head = NULL;
        ssize_t slen;
@@ -1290,6 +1284,11 @@ static ssize_t explode_xlat(UNUSED void *instance, REQUEST *request,
        char const *p = fmt;
        char delim;
 
+       /*
+        *  Trim whitespace
+        */
+       while (isspace(*p) && p++);
+
        slen = tmpl_from_attr_substr(&vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
        if (slen <= 0) {
                REDEBUG("%s", fr_strerror());
@@ -1346,9 +1345,9 @@ static ssize_t explode_xlat(UNUSED void *instance, REQUEST *request,
                                continue;
                        }
 
-                       new = pairalloc(talloc_parent(vp), vp->da);
+                       new = fr_pair_afrom_da(talloc_parent(vp), vp->da);
                        if (!new) {
-                               pairfree(&head);
+                               fr_pair_list_free(&head);
                                return -1;
                        }
                        new->tag = vp->tag;
@@ -1360,7 +1359,7 @@ static ssize_t explode_xlat(UNUSED void *instance, REQUEST *request,
 
                                buff = talloc_array(new, uint8_t, q - p);
                                memcpy(buff, p, q - p);
-                               pairmemsteal(new, buff);
+                               fr_pair_value_memsteal(new, buff);
                        }
                                break;
 
@@ -1371,7 +1370,7 @@ static ssize_t explode_xlat(UNUSED void *instance, REQUEST *request,
                                buff = talloc_array(new, char, (q - p) + 1);
                                memcpy(buff, p, q - p);
                                buff[q - p] = '\0';
-                               pairstrsteal(new, (char *)buff);
+                               fr_pair_value_strsteal(new, (char *)buff);
                        }
                                break;
 
@@ -1481,14 +1480,14 @@ static ssize_t next_time_xlat(UNUSED void *instance, REQUEST *request,
  *     Parse the 3 arguments to lpad / rpad.
  */
 static bool parse_pad(REQUEST *request, char const *fmt,
-                      value_pair_tmpl_t **pvpt, size_t *plength,
+                      vp_tmpl_t **pvpt, size_t *plength,
                       char *fill)
 {
        ssize_t slen;
        unsigned long length;
        char const *p;
        char *end;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        *fill = ' ';            /* the default */
 
@@ -1500,11 +1499,11 @@ static bool parse_pad(REQUEST *request, char const *fmt,
                return false;
        }
 
-       vpt = talloc(request, value_pair_tmpl_t);
+       vpt = talloc(request, vp_tmpl_t);
        if (!vpt) return false;
 
        slen = tmpl_from_attr_substr(vpt, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
-       if (slen < 0) {
+       if (slen <= 0) {
                talloc_free(vpt);
                RDEBUG("Failed expanding string: %s", fr_strerror());
                return false;
@@ -1546,13 +1545,13 @@ static bool parse_pad(REQUEST *request, char const *fmt,
 
                *fill = *p;
        }
-       
+
        *pvpt = vpt;
        *plength = length;
 
        return true;
 }
-                     
+
 
 /** left pad a string
  *
@@ -1564,7 +1563,7 @@ static ssize_t lpad_xlat(UNUSED void *instance, REQUEST *request,
        char fill;
        size_t pad;
        ssize_t len;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        *out = '\0';
        if (!parse_pad(request, fmt, &vpt, &pad, &fill)) {
@@ -1606,7 +1605,7 @@ static ssize_t rpad_xlat(UNUSED void *instance, REQUEST *request,
        char fill;
        size_t pad;
        ssize_t len;
-       value_pair_tmpl_t *vpt;
+       vp_tmpl_t *vpt;
 
        *out = '\0';
 
@@ -1648,7 +1647,7 @@ static ssize_t rpad_xlat(UNUSED void *instance, REQUEST *request,
  *     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)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        rlm_expr_t *inst = instance;
 
@@ -1659,9 +1658,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        xlat_register(inst->xlat_name, expr_xlat, NULL, inst);
 
-       /*
-        *      FIXME: unregister these, too
-        */
        xlat_register("rand", rand_xlat, NULL, inst);
        xlat_register("randstr", randstr_xlat, NULL, inst);
        xlat_register("urlquote", urlquote_xlat, NULL, inst);
@@ -1707,17 +1703,9 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  */
 extern module_t rlm_expr;
 module_t rlm_expr = {
-       RLM_MODULE_INIT,
-       "expr",                         /* Name */
-       0,      /* type */
-       sizeof(rlm_expr_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* pre-accounting */
-               NULL                    /* accounting */
-       },
+       .magic          = RLM_MODULE_INIT,
+       .name           = "expr",
+       .inst_size      = sizeof(rlm_expr_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
 };
index 81ea8fd..4ca3dfd 100644 (file)
@@ -36,34 +36,34 @@ typedef struct rlm_files_t {
        char const *key;
 
        char const *filename;
-       fr_hash_table_t *common;
+       rbtree_t *common;
 
        /* autz */
        char const *usersfile;
-       fr_hash_table_t *users;
+       rbtree_t *users;
 
 
        /* authenticate */
        char const *auth_usersfile;
-       fr_hash_table_t *auth_users;
+       rbtree_t *auth_users;
 
        /* preacct */
        char const *acctusersfile;
-       fr_hash_table_t *acctusers;
+       rbtree_t *acctusers;
 
 #ifdef WITH_PROXY
        /* pre-proxy */
        char const *preproxy_usersfile;
-       fr_hash_table_t *preproxy_users;
+       rbtree_t *preproxy_users;
 
        /* post-proxy */
        char const *postproxy_usersfile;
-       fr_hash_table_t *postproxy_users;
+       rbtree_t *postproxy_users;
 #endif
 
        /* post-authenticate */
        char const *postauth_usersfile;
-       fr_hash_table_t *postauth_users;
+       rbtree_t *postauth_users;
 } rlm_files_t;
 
 
@@ -73,7 +73,7 @@ typedef struct rlm_files_t {
 static int fall_through(VALUE_PAIR *vp)
 {
        VALUE_PAIR *tmp;
-       tmp = pairfind(vp, PW_FALL_THROUGH, 0, TAG_ANY);
+       tmp = fr_pair_find_by_num(vp, PW_FALL_THROUGH, 0, TAG_ANY);
 
        return tmp ? tmp->vp_integer : 0;
 }
@@ -88,41 +88,28 @@ static const CONF_PARSER module_config[] = {
 #endif
        { "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" },
+       { "compat", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_files_t, compat_mode), NULL },
        { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_files_t, key), NULL },
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 
-static uint32_t pairlist_hash(void const *data)
-{
-       return fr_hash_string(((PAIR_LIST const *)data)->name);
-}
-
 static int pairlist_cmp(void const *a, void const *b)
 {
        return strcmp(((PAIR_LIST const *)a)->name,
                      ((PAIR_LIST const *)b)->name);
 }
 
-static void my_pairlist_free(void *data)
-{
-       PAIR_LIST *pl = data;
-
-       pairlist_free(&pl);
-}
-
-
-static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t **pht, char const *compat_mode_str)
+static int getusersfile(TALLOC_CTX *ctx, char const *filename, rbtree_t **ptree, char const *compat_mode_str)
 {
        int rcode;
        PAIR_LIST *users = NULL;
        PAIR_LIST *entry, *next;
-       fr_hash_table_t *ht, *tailht;
-       int order = 0;
+       PAIR_LIST *user_list, *default_list, **default_tail;
+       rbtree_t *tree;
 
        if (!filename) {
-               *pht = NULL;
+               *ptree = NULL;
                return 0;
        }
 
@@ -135,12 +122,12 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t *
         *      Walk through the 'users' file list, if we're debugging,
         *      or if we're in compat_mode.
         */
-       if ((debug_flag) ||
-           (strcmp(compat_mode_str, "cistron") == 0)) {
+       if ((rad_debug_lvl) ||
+           (compat_mode_str && (strcmp(compat_mode_str, "cistron") == 0))) {
                VALUE_PAIR *vp;
                bool compat_mode = false;
 
-               if (strcmp(compat_mode_str, "cistron") == 0) {
+               if (compat_mode_str && (strcmp(compat_mode_str, "cistron") == 0)) {
                        compat_mode = true;
                }
 
@@ -246,86 +233,85 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t *
 
                        entry = entry->next;
                }
-
        }
 
-       ht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
-                                   my_pairlist_free);
-       if (!ht) {
+       tree = rbtree_create(ctx, pairlist_cmp, NULL, RBTREE_FLAG_NONE);
+       if (!tree) {
                pairlist_free(&users);
                return -1;
        }
 
-       tailht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
-                                       NULL);
-       if (!tailht) {
-               fr_hash_table_free(ht);
-               pairlist_free(&users);
-               return -1;
-       }
+       default_list = NULL;
+       default_tail = &default_list;
 
        /*
-        *      Now that we've read it in, put the entries into a hash
-        *      for faster access.
+        *      We've read the entries in linearly, but putting them
+        *      into an indexed data structure would be much faster.
+        *      Let's go fix that now.
         */
        for (entry = users; entry != NULL; entry = next) {
-               PAIR_LIST *tail;
-
+               /*
+                *      Remove this entry from the input list.
+                */
                next = entry->next;
                entry->next = NULL;
-               entry->order = order++;
+               (void) talloc_steal(tree, entry);
+
+               /*
+                *      DEFAULT entries get their own list.
+                */
+               if (strcmp(entry->name, "DEFAULT") == 0) {
+                       if (!default_list) {
+                               default_list = entry;
+
+                               /*
+                                *      Insert the first DEFAULT into the tree.
+                                */
+                               if (!rbtree_insert(tree, entry)) {
+                               error:
+                                       pairlist_free(&entry);
+                                       pairlist_free(&next);
+                                       rbtree_free(tree);
+                                       return -1;
+                               }
+
+                       } else {
+                               /*
+                                *      Tack this entry onto the tail
+                                *      of the DEFAULT list.
+                                */
+                               *default_tail = entry;
+                       }
+
+                       default_tail = &entry->next;
+                       continue;
+               }
 
                /*
-                *      Insert it into the hash table, and remember
-                *      the tail of the linked list.
+                *      Not DEFAULT, must be a normal user.
                 */
-               tail = fr_hash_table_finddata(tailht, entry);
-               if (!tail) {
+               user_list = rbtree_finddata(tree, entry);
+               if (!user_list) {
                        /*
-                        *      Insert it into the head & tail.
+                        *      Insert the first one.
                         */
-                       if (!fr_hash_table_insert(ht, entry) ||
-                           !fr_hash_table_insert(tailht, entry)) {
-                               pairlist_free(&next);
-                               fr_hash_table_free(ht);
-                               fr_hash_table_free(tailht);
-                               return -1;
-                       }
+                       if (!rbtree_insert(tree, entry)) goto error;
                } else {
-                       tail->next = entry;
-                       if (!fr_hash_table_replace(tailht, entry)) {
-                               pairlist_free(&next);
-                               fr_hash_table_free(ht);
-                               fr_hash_table_free(tailht);
-                               return -1;
-                       }
+                       /*
+                        *      Find the tail of this list, and add it
+                        *      there.
+                        */
+                       while (user_list->next) user_list = user_list->next;
+
+                       user_list->next = entry;
                }
        }
 
-       fr_hash_table_free(tailht);
-       *pht = ht;
+       *ptree = tree;
 
        return 0;
 }
 
-/*
- *     Clean up.
- */
-static int mod_detach(void *instance)
-{
-       rlm_files_t *inst = instance;
-       fr_hash_table_free(inst->common);
-       fr_hash_table_free(inst->users);
-       fr_hash_table_free(inst->acctusers);
-#ifdef WITH_PROXY
-       fr_hash_table_free(inst->preproxy_users);
-       fr_hash_table_free(inst->postproxy_users);
-#endif
-       fr_hash_table_free(inst->auth_users);
-       fr_hash_table_free(inst->postauth_users);
-       return 0;
-}
-
 
 
 /*
@@ -336,7 +322,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
        rlm_files_t *inst = instance;
 
 #undef READFILE
-#define READFILE(_x, _y) do { if (getusersfile(inst, inst->_x, &inst->_y, inst->compat_mode) != 0) { ERROR("Failed reading %s", inst->_x); mod_detach(inst);return -1;} } while (0)
+#define READFILE(_x, _y) do { if (getusersfile(inst, inst->_x, &inst->_y, inst->compat_mode) != 0) { ERROR("Failed reading %s", inst->_x); return -1;} } while (0)
 
        READFILE(filename, common);
        READFILE(usersfile, users);
@@ -356,7 +342,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
 /*
  *     Common code called by everything below.
  */
-static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *filename, fr_hash_table_t *ht,
+static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *filename, rbtree_t *tree,
                               RADIUS_PACKET *request_packet, RADIUS_PACKET *reply_packet)
 {
        char const      *name, *match;
@@ -383,12 +369,12 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *
                name = len ? buffer : "NONE";
        }
 
-       if (!ht) return RLM_MODULE_NOOP;
+       if (!tree) return RLM_MODULE_NOOP;
 
        my_pl.name = name;
-       user_pl = fr_hash_table_finddata(ht, &my_pl);
+       user_pl = rbtree_finddata(tree, &my_pl);
        my_pl.name = "DEFAULT";
-       default_pl = fr_hash_table_finddata(ht, &my_pl);
+       default_pl = rbtree_finddata(tree, &my_pl);
 
        /*
         *      Find the entry for the user.
@@ -398,6 +384,10 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *
                VALUE_PAIR *vp;
                PAIR_LIST const *pl;
 
+               /*
+                *      Figure out which entry to match on.
+                */
+
                if (!default_pl && user_pl) {
                        pl = user_pl;
                        match = name;
@@ -408,7 +398,7 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *
                        match = "DEFAULT";
                        default_pl = default_pl->next;
 
-               } else if (user_pl->order < default_pl->order) {
+               } else if (user_pl->lineno < default_pl->lineno) {
                        pl = user_pl;
                        match = name;
                        user_pl = user_pl->next;
@@ -419,13 +409,13 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *
                        default_pl = default_pl->next;
                }
 
-               check_tmp = paircopy(request, pl->check);
+               check_tmp = fr_pair_list_copy(request, pl->check);
                for (vp = fr_cursor_init(&cursor, &check_tmp);
                     vp;
                     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);
+                               fr_pair_list_free(&check_tmp);
                                continue;
                        }
                }
@@ -435,10 +425,10 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *
                        found = true;
 
                        /* ctx may be reply or proxy */
-                       reply_tmp = paircopy(reply_packet, pl->reply);
+                       reply_tmp = fr_pair_list_copy(reply_packet, pl->reply);
                        radius_pairmove(request, &reply_packet->vps, reply_tmp, true);
-                       pairmove(request, &request->config_items, &check_tmp);
-                       pairfree(&check_tmp);
+                       fr_pair_list_move(request, &request->config, &check_tmp);
+                       fr_pair_list_free(&check_tmp);
 
                        /*
                         *      Fallthrough?
@@ -451,7 +441,7 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *
        /*
         *      Remove server internal parameters.
         */
-       pairdelete(&reply_packet->vps, PW_FALL_THROUGH, 0, TAG_ANY);
+       fr_pair_delete_by_num(&reply_packet->vps, PW_FALL_THROUGH, 0, TAG_ANY);
 
        /*
         *      See if we succeeded.
@@ -482,7 +472,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
 
 /*
  *     Pre-Accounting - read the acct_users file for check_items and
- *     config_items. Reply items are Not Recommended(TM) in acct_users,
+ *     config. Reply items are Not Recommended(TM) in acct_users,
  *     except for Fallthrough, which should work
  */
 static rlm_rcode_t CC_HINT(nonnull) mod_preacct(void *instance, REQUEST *request)
@@ -536,26 +526,22 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
 /* globally exported name */
 extern module_t rlm_files;
 module_t rlm_files = {
-       RLM_MODULE_INIT,
-       "files",
-       RLM_TYPE_HUP_SAFE,
-       sizeof(rlm_files_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,  /* authorization */
-               mod_preacct,            /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "files",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_files_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preacct,
+
 #ifdef WITH_PROXY
-               mod_pre_proxy,          /* pre-proxy */
-               mod_post_proxy,         /* post-proxy */
-#else
-               NULL, NULL,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
+               [MOD_POST_PROXY]        = mod_post_proxy,
 #endif
-               mod_post_auth           /* post-auth */
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
 
index 69c8577..c0ce436 100644 (file)
@@ -84,8 +84,7 @@ static const CONF_PARSER mod_config[] = {
 
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 static ssize_t xlat_idna(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
@@ -131,7 +130,7 @@ static ssize_t xlat_idna(void *instance, REQUEST *request, char const *fmt, char
        return len;
 }
 
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        rlm_idn_t *inst = instance;
        char const *xlat_name;
@@ -150,25 +149,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
 extern module_t rlm_idn;
 module_t rlm_idn = {
-       RLM_MODULE_INIT,
-       "idn",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_idn_t),
-       mod_config,                     /* CONF_PARSER */
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
-#ifdef WITH_COA
-               , NULL,
-               NULL
-#endif
-       },
+       .magic          = RLM_MODULE_INIT,
+       .name           = "idn",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_idn_t),
+       .config         = mod_config,
+       .bootstrap      = mod_bootstrap
 };
index 1592ecc..7762b15 100644 (file)
@@ -128,8 +128,7 @@ static const CONF_PARSER module_config[] = {
 
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -339,7 +338,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        char            xlat_str[MAX_STRING_LEN];
        int             ret;
 
-       vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(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;
@@ -462,7 +461,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *  Check if Pool-Name attribute exists. If it exists check our name and
         *  run only if they match
         */
-       vp = pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->config, 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;
@@ -474,7 +473,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        /*
         *  Find the caller id
         */
-       vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY);
        if (vp != NULL) {
                cli = vp->vp_strvalue;
        }
@@ -557,7 +556,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *  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) {
+       if (fr_pair_find_by_num(request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY) != NULL) {
                RDEBUG("Found IP address attribute in reply attribute list");
                if (!inst->override) {
                        RDEBUG("override is set to no. Return NOOP");
@@ -565,7 +564,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                }
 
                RDEBUG("Override supplied IP address");
-               pairdelete(&request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY);
+               fr_pair_delete_by_num(&request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY);
        }
 
        /*
@@ -718,14 +717,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                free(key_datum.dptr);
                entry.active = 1;
                entry.timestamp = request->timestamp;
-               if ((vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY)) != NULL) {
+               if ((vp = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY)) != NULL) {
                        entry.timeout = (time_t) vp->vp_integer;
 #ifdef WITH_DHCP
                        if (dhcp) {
-                               vp = radius_paircreate(request->reply, &request->reply->vps,
+                               vp = radius_pair_create(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);
+                               fr_pair_delete_by_num(&request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
                        }
 #endif
                } else {
@@ -773,7 +772,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                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->reply, &request->reply->vps,
+               vp = radius_pair_create(request->reply, &request->reply->vps,
                                       attr_ipaddr, vendor_ipaddr);
                vp->vp_ipaddr = entry.ipaddr;
 
@@ -781,8 +780,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                 *      If there is no Framed-Netmask attribute in the
                 *      reply, add one
                 */
-               if (pairfind(request->reply->vps, attr_ipmask, vendor_ipaddr, TAG_ANY) == NULL) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+               if (fr_pair_find_by_num(request->reply->vps, attr_ipmask, vendor_ipaddr, TAG_ANY) == NULL) {
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               attr_ipmask, vendor_ipaddr);
                        vp->vp_ipaddr = ntohl(inst->netmask);
                }
@@ -818,21 +817,16 @@ static int mod_detach(void *instance)
  */
 extern module_t rlm_ippool;
 module_t rlm_ippool = {
-       RLM_MODULE_INIT,
-       "ippool",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_ippool_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               NULL,                   /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* preaccounting */
-               mod_accounting, /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "ippool",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_ippool_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
index b70d0d1..153a10c 100644 (file)
@@ -69,18 +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
+#  ifdef HAVE_KRB5_FREE_ERROR_MESSAGE
                krb5_free_error_message(context, msg);
-#elif defined(HAVE_KRB5_FREE_ERROR_STRING)
+#  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
+#    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);
        }
@@ -99,14 +99,10 @@ char const *rlm_krb5_error(krb5_context context, krb5_error_code code)
 static int _mod_conn_free(rlm_krb5_handle_t *conn) {
        krb5_free_context(conn->context);
 
-       if (conn->keytab) {
-               krb5_kt_close(conn->context, conn->keytab);
-       }
+       if (conn->keytab) krb5_kt_close(conn->context, conn->keytab);
 
 #ifdef HEIMDAL_KRB5
-       if (conn->ccache) {
-               krb5_cc_destroy(conn->context, conn->ccache);
-       }
+       if (conn->ccache) krb5_cc_destroy(conn->context, conn->ccache);
 #endif
 
        return 0;
@@ -158,9 +154,7 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
        krb5_verify_opt_set_keytab(&conn->options, conn->keytab);
        krb5_verify_opt_set_secure(&conn->options, true);
 
-       if (inst->service) {
-               krb5_verify_opt_set_service(&conn->options, inst->service);
-       }
+       if (inst->service) krb5_verify_opt_set_service(&conn->options, inst->service);
 #else
        krb5_verify_init_creds_opt_set_ap_req_nofail(inst->vic_options, true);
 #endif
index 10c2ac9..c80ec25 100644 (file)
@@ -34,7 +34,7 @@ RCSID("$Id$")
 static const CONF_PARSER module_config[] = {
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 static int mod_detach(void *instance)
@@ -44,23 +44,16 @@ static int mod_detach(void *instance)
 #ifndef HEIMDAL_KRB5
        talloc_free(inst->vic_options);
 
-       if (inst->gic_options) {
-               krb5_get_init_creds_opt_free(inst->context, inst->gic_options);
-       }
-
-       if (inst->server) {
-               krb5_free_principal(inst->context, inst->server);
-       }
+       if (inst->gic_options) krb5_get_init_creds_opt_free(inst->context, inst->gic_options);
+       if (inst->server) krb5_free_principal(inst->context, inst->server);
 #endif
 
        /* Don't free hostname, it's just a pointer into service_princ */
        talloc_free(inst->service);
 
-       if (inst->context) {
-               krb5_free_context(inst->context);
-       }
+       if (inst->context) krb5_free_context(inst->context);
 #ifdef KRB5_IS_THREAD_SAFE
-       fr_connection_pool_delete(inst->pool);
+       fr_connection_pool_free(inst->pool);
 #endif
 
        return 0;
@@ -109,9 +102,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 
        inst->xlat_name = cf_section_name2(conf);
-       if (!inst->xlat_name) {
-               inst->xlat_name = cf_section_name1(conf);
-       }
+       if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
 
        ret = krb5_init_context(&inst->context);
        if (ret) {
@@ -144,10 +135,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 
 #ifdef HEIMDAL_KRB5
-       if (inst->hostname) {
-               DEBUG("rlm_krb5 (%s): Ignoring hostname component of service principal \"%s\", not "
-                     "needed/supported by Heimdal", inst->xlat_name, inst->hostname);
-       }
+       if (inst->hostname) DEBUG("rlm_krb5 (%s): Ignoring hostname component of service principal \"%s\", not "
+                                 "needed/supported by Heimdal", inst->xlat_name, inst->hostname);
 #else
 
        /*
@@ -337,12 +326,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
 
        krb5_principal client;
 
-#ifdef KRB5_IS_THREAD_SAFE
+#  ifdef KRB5_IS_THREAD_SAFE
        conn = fr_connection_get(inst->pool);
        if (!conn) return RLM_MODULE_FAIL;
-#else
+#  else
        conn = inst->conn;
-#endif
+#  endif
 
        /*
         *      Zero out local storage
@@ -389,9 +378,9 @@ cleanup:
                krb5_free_principal(conn->context, client);
        }
 
-#ifdef KRB5_IS_THREAD_SAFE
+#  ifdef KRB5_IS_THREAD_SAFE
        fr_connection_release(inst->pool, conn);
-#endif
+#  endif
        return rcode;
 }
 
@@ -414,12 +403,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
 
        rad_assert(inst->context);
 
-#ifdef KRB5_IS_THREAD_SAFE
+#  ifdef KRB5_IS_THREAD_SAFE
        conn = fr_connection_get(inst->pool);
        if (!conn) return RLM_MODULE_FAIL;
-#else
+#  else
        conn = inst->conn;
-#endif
+#  endif
 
        /*
         *      Zero out local storage
@@ -448,19 +437,15 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
 
        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) {
-               rcode = krb5_process_error(request, conn, ret);
-       }
+       if (ret) rcode = krb5_process_error(request, conn, ret);
 
 cleanup:
-       if (client) {
-               krb5_free_principal(conn->context, client);
-       }
+       if (client) krb5_free_principal(conn->context, client);
        krb5_free_cred_contents(conn->context, &init_creds);
 
-#ifdef KRB5_IS_THREAD_SAFE
+#  ifdef KRB5_IS_THREAD_SAFE
        fr_connection_release(inst->pool, conn);
-#endif
+#  endif
        return rcode;
 }
 
@@ -468,25 +453,18 @@ cleanup:
 
 extern module_t rlm_krb5;
 module_t rlm_krb5 = {
-       RLM_MODULE_INIT,
-       "krb5",
-       RLM_TYPE_HUP_SAFE
+       .magic          = RLM_MODULE_INIT,
+       .name           = "krb5",
+       .type           = RLM_TYPE_HUP_SAFE
 #ifdef KRB5_IS_THREAD_SAFE
        | RLM_TYPE_THREAD_SAFE
 #endif
        ,
-       sizeof(rlm_krb5_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authenticate */
-               NULL,                   /* authorize */
-               NULL,                   /* pre-accounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .inst_size      = sizeof(rlm_krb5_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate
        },
 };
index 0855a08..daa64ec 100644 (file)
@@ -4,7 +4,7 @@ ifneq "$(TARGETNAME)" ""
 TARGET         := $(TARGETNAME).a
 endif
 
-SOURCES                := $(TARGETNAME).c attrmap.c ldap.c clients.c groups.c edir.c
+SOURCES                := $(TARGETNAME).c attrmap.c ldap.c clients.c groups.c edir.c @SASL@
 
 SRC_CFLAGS     := @mod_cflags@
 TGT_LDLIBS     := @mod_ldflags@
index abbd770..98d914f 100644 (file)
@@ -33,9 +33,9 @@
  *
  * @see map_to_vp
  */
-static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, void *ctx)
+int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx)
 {
-       rlm_ldap_result_t *self = ctx;
+       rlm_ldap_result_t *self = uctx;
        VALUE_PAIR *head = NULL, *vp;
        vp_cursor_t cursor;
        int i;
@@ -56,12 +56,12 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
         */
        case TMPL_TYPE_LIST:
                for (i = 0; i < self->count; i++) {
-                       value_pair_map_t *attr = NULL;
+                       vp_map_t *attr = NULL;
 
                        RDEBUG3("Parsing valuepair string \"%s\"", self->values[i]->bv_val);
-                       if (map_afrom_attr_str(request, &attr, self->values[i]->bv_val,
-                                            map->lhs->tmpl_request, map->lhs->tmpl_list,
-                                            REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+                       if (map_afrom_attr_str(ctx, &attr, self->values[i]->bv_val,
+                                              map->lhs->tmpl_request, map->lhs->tmpl_list,
+                                              REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
                                RWDEBUG("Failed parsing \"%s\" as valuepair (%s), skipping...", fr_strerror(),
                                        self->values[i]->bv_val);
                                continue;
@@ -85,7 +85,7 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
                                goto next_pair;
                        }
 
-                       if (map_to_vp(&vp, request, attr, NULL) < 0) {
+                       if (map_to_vp(request, &vp, request, attr, NULL) < 0) {
                                RWDEBUG("Failed creating attribute for valuepair \"%s\", skipping...",
                                        self->values[i]->bv_val);
                                goto next_pair;
@@ -93,6 +93,11 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
 
                        fr_cursor_merge(&cursor, vp);
                        talloc_free(attr);
+
+                       /*
+                        *      Only process the first value, unless the operator is +=
+                        */
+                       if (map->op != T_OP_ADD) break;
                }
                break;
 
@@ -105,10 +110,10 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
                for (i = 0; i < self->count; i++) {
                        if (!self->values[i]->bv_len) continue;
 
-                       vp = pairalloc(request, map->lhs->tmpl_da);
+                       vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
                        rad_assert(vp);
 
-                       if (pairparsevalue(vp, self->values[i]->bv_val, self->values[i]->bv_len) < 0) {
+                       if (fr_pair_value_from_str(vp, self->values[i]->bv_val, self->values[i]->bv_len) < 0) {
                                char *escaped;
 
                                escaped = fr_aprints(vp, self->values[i]->bv_val, self->values[i]->bv_len, '"');
@@ -121,6 +126,11 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
 
                        vp->op = map->op;
                        fr_cursor_insert(&cursor, vp);
+
+                       /*
+                        *      Only process the first value, unless the operator is +=
+                        */
+                       if (map->op != T_OP_ADD) break;
                }
                break;
 
@@ -133,9 +143,9 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
        return 0;
 }
 
-int rlm_ldap_map_verify(value_pair_map_t *map, void *instance)
+int rlm_ldap_map_verify(vp_map_t *map, void *instance)
 {
-       ldap_instance_t *inst = instance;
+       rlm_ldap_t *inst = instance;
 
        /*
         *      Destinations where we can put the VALUE_PAIRs we
@@ -146,6 +156,10 @@ int rlm_ldap_map_verify(value_pair_map_t *map, void *instance)
        case TMPL_TYPE_ATTR:
                break;
 
+       case TMPL_TYPE_ATTR_UNDEFINED:
+               cf_log_err(map->ci, "Unknown attribute %s", map->lhs->tmpl_unknown_name);
+               return -1;
+
        default:
                cf_log_err(map->ci, "Left hand side of map must be an attribute or list, not a %s",
                           fr_int2str(tmpl_names, map->lhs->type, "<INVALID>"));
@@ -163,6 +177,10 @@ int rlm_ldap_map_verify(value_pair_map_t *map, void *instance)
        case TMPL_TYPE_LITERAL:
                break;
 
+       case TMPL_TYPE_ATTR_UNDEFINED:
+               cf_log_err(map->ci, "Unknown attribute %s", map->rhs->tmpl_unknown_name);
+               return -1;
+
        default:
                cf_log_err(map->ci, "Right hand side of map must be an xlat, attribute, exec, or literal, not a %s",
                           fr_int2str(tmpl_names, map->rhs->type, "<INVALID>"));
@@ -221,111 +239,43 @@ int rlm_ldap_map_verify(value_pair_map_t *map, void *instance)
        return 0;
 }
 
-/** Free attribute map values
- *
- */
-void rlm_ldap_map_xlat_free(rlm_ldap_map_xlat_t const *expanded)
-{
-       value_pair_map_t const *map;
-       unsigned int total = 0;
-
-       char const *name;
-
-       for (map = expanded->maps; map != NULL; map = map->next) {
-               name = expanded->attrs[total++];
-               if (!name) return;
-
-               switch (map->rhs->type) {
-               case TMPL_TYPE_EXEC:
-               case TMPL_TYPE_XLAT:
-               case TMPL_TYPE_ATTR:
-                       rad_const_free(name);
-                       break;
-               default:
-                       break;
-               }
-       }
-}
-
 /** Expand values in an attribute map where needed
  *
+ * @param[out] expanded array of attributes. Need not be initialised (we'll initialise).
+ * @param[in] request The current request.
+ * @param[in] maps to expand.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
  */
-int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded)
+int rlm_ldap_map_expand(rlm_ldap_map_exp_t *expanded, REQUEST *request, vp_map_t const *maps)
 {
-       value_pair_map_t const *map;
-       unsigned int total = 0;
+       vp_map_t const  *map;
+       unsigned int    total = 0;
 
-       VALUE_PAIR *found, **from = NULL;
-       REQUEST *context;
+       TALLOC_CTX      *ctx = NULL;
+       char const      *attr;
+       char            attr_buff[1024 + 1];    /* X.501 says we need to support at least 1024 chars for attr names */
 
        for (map = maps; map != NULL; map = map->next) {
-               switch (map->rhs->type) {
-               case TMPL_TYPE_XLAT:
-               {
-                       ssize_t len;
-                       char *exp = NULL;
-
-                       len = radius_axlat(&exp, request, map->rhs->name, NULL, NULL);
-                       if (len < 0) {
-                               RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->rhs->name);
-
-                               goto error;
-                       }
-
-                       expanded->attrs[total++] = exp;
-                       break;
-               }
-
-               case TMPL_TYPE_ATTR:
-                       context = request;
-
-                       if (radius_request(&context, map->rhs->tmpl_request) == 0) {
-                               from = radius_list(context, map->rhs->tmpl_list);
-                       }
-                       if (!from) continue;
-
-                       found = pair_find_by_da(*from, map->rhs->tmpl_da, TAG_ANY);
-                       if (!found) continue;
-
-                       expanded->attrs[total++] = talloc_typed_strdup(request, found->vp_strvalue);
-                       break;
-
-               case TMPL_TYPE_EXEC:
-               {
-                       char answer[1024];
-                       VALUE_PAIR **input_pairs = NULL;
-                       int result;
-
-                       input_pairs = radius_list(request, PAIR_LIST_REQUEST);
-                       result = radius_exec_program(answer, sizeof(answer), NULL, request,
-                                                    map->rhs->name, input_pairs ? *input_pairs : NULL,
-                                                    true, true, EXEC_TIMEOUT);
-                       if (result != 0) {
-                               return -1;
-                       }
-
-                       expanded->attrs[total++] = talloc_typed_strdup(request, answer);
+               if (tmpl_expand(&attr, attr_buff, sizeof(attr_buff), request, map->rhs, NULL, NULL) < 0) {
+                       RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->rhs->name);
+                       TALLOC_FREE(ctx);
+                       return -1;
                }
-                       break;
-
-               case TMPL_TYPE_LITERAL:
-                       expanded->attrs[total++] = map->rhs->name;
-                       break;
-
-               default:
-                       rad_assert(0);
-               error:
-                       expanded->attrs[total] = NULL;
 
-                       rlm_ldap_map_xlat_free(expanded);
-
-                       return -1;
+               /*
+                *      Dynamic value
+                */
+               if (attr == attr_buff) {
+                       if (!ctx) ctx = talloc_new(NULL);
+                       expanded->attrs[total++] = talloc_strdup(ctx, attr_buff);
+                       continue;
                }
+               expanded->attrs[total++] = attr;
        }
-
-       rad_assert(total < LDAP_MAX_ATTRMAP);
-
        expanded->attrs[total] = NULL;
+       expanded->ctx = ctx;    /* Freeing this frees any dynamic values */
        expanded->count = total;
        expanded->maps = maps;
 
@@ -345,19 +295,20 @@ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_m
  * @param[in] handle associated with entry.
  * @param[in] expanded attributes (rhs of map).
  * @param[in] entry to retrieve attributes from.
- * @return number of maps successfully applied, or -1 on error.
+ * @return
+ *     - Number of maps successfully applied.
+ *     - -1 on failure.
  */
-int rlm_ldap_map_do(const ldap_instance_t *inst, REQUEST *request, LDAP *handle,
-                   rlm_ldap_map_xlat_t const *expanded, LDAPMessage *entry)
+int rlm_ldap_map_do(const rlm_ldap_t *inst, REQUEST *request, LDAP *handle,
+                   rlm_ldap_map_exp_t const *expanded, LDAPMessage *entry)
 {
-       value_pair_map_t const  *map;
+       vp_map_t const  *map;
        unsigned int            total = 0;
        int                     applied = 0;    /* How many maps have been applied to the current request */
 
        rlm_ldap_result_t       result;
        char const              *name;
 
-       RINDENT();
        for (map = expanded->maps; map != NULL; map = map->next) {
                int ret;
 
@@ -395,7 +346,7 @@ int rlm_ldap_map_do(const ldap_instance_t *inst, REQUEST *request, LDAP *handle,
        next:
                ldap_value_free_len(result.values);
        }
-       REXDENT();
+
 
        /*
         *      Retrieve any valuepair attributes from the result, these are generic values specifying
@@ -408,9 +359,8 @@ int rlm_ldap_map_do(const ldap_instance_t *inst, REQUEST *request, LDAP *handle,
                values = ldap_get_values_len(handle, entry, inst->valuepair_attr);
                count = ldap_count_values_len(values);
 
-               RINDENT();
                for (i = 0; i < count; i++) {
-                       value_pair_map_t *attr;
+                       vp_map_t *attr;
                        char *value;
 
                        value = rlm_ldap_berval_to_string(request, values[i]);
@@ -431,82 +381,8 @@ int rlm_ldap_map_do(const ldap_instance_t *inst, REQUEST *request, LDAP *handle,
                        talloc_free(attr);
                        talloc_free(value);
                }
-               REXDENT();
                ldap_value_free_len(values);
        }
 
        return applied;
 }
-
-/** Search for and apply an LDAP profile
- *
- * LDAP profiles are mapped using the same attribute map as user objects, they're used to add common sets of attributes
- * to the request.
- *
- * @param[in] inst rlm_ldap configuration.
- * @param[in] request Current request.
- * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
- * @param[in] dn of profile object to apply.
- * @param[in] expanded Structure containing a list of xlat expanded attribute names and mapping information.
- * @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)
-{
-       rlm_rcode_t     rcode = RLM_MODULE_OK;
-       ldap_rcode_t    status;
-       LDAPMessage     *result = NULL, *entry = NULL;
-       int             ldap_errno;
-       LDAP            *handle = (*pconn)->handle;
-       char const      *filter;
-       char            filter_buff[LDAP_MAX_FILTER_STR_LEN];
-
-       rad_assert(inst->profile_filter);       /* We always have a default filter set */
-
-       if (!dn || !*dn) return RLM_MODULE_OK;
-
-       if (tmpl_expand(&filter, filter_buff, sizeof(filter_buff), request,
-                       inst->profile_filter, rlm_ldap_escape_func, NULL) < 0) {
-               REDEBUG("Failed creating profile filter");
-
-               return RLM_MODULE_INVALID;
-       }
-
-       status = rlm_ldap_search(inst, request, pconn, dn, LDAP_SCOPE_BASE, filter, expanded->attrs, &result);
-       switch (status) {
-       case LDAP_PROC_SUCCESS:
-               break;
-
-       case LDAP_PROC_BAD_DN:
-       case LDAP_PROC_NO_RESULT:
-               RDEBUG("Profile object \"%s\" not found", dn);
-               return RLM_MODULE_NOTFOUND;
-
-       default:
-               return RLM_MODULE_FAIL;
-       }
-
-       rad_assert(*pconn);
-       rad_assert(result);
-
-       entry = ldap_first_entry(handle, result);
-       if (!entry) {
-               ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
-               REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
-
-               rcode = RLM_MODULE_NOTFOUND;
-
-               goto free_result;
-       }
-
-       RDEBUG("Processing profile attributes");
-       RINDENT();
-       if (rlm_ldap_map_do(inst, request, handle, expanded, entry) > 0) rcode = RLM_MODULE_UPDATED;
-       REXDENT();
-
-free_result:
-       ldap_msgfree(result);
-
-       return rcode;
-}
-
index 6b3cede..8654475 100644 (file)
@@ -20,8 +20,8 @@
  * @brief LDAP module dynamic clients.
  *
  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
- * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
- * @copyright 2013 The FreeRADIUS Server Project.
+ * @copyright 2013,2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013-2015 The FreeRADIUS Server Project.
  */
 #include       <freeradius-devel/rad_assert.h>
 #include       <ctype.h>
@@ -38,7 +38,9 @@
  * @param[out] values array of char pointers.
  * @param[in,out] idx records current array offset.
  * @param[in] cs to iterate over.
- * @return 0 on success else -1 on error.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
  */
 static int rlm_ldap_client_get_attrs(char const **values, int *idx, CONF_SECTION const *cs)
 {
@@ -65,75 +67,39 @@ static int rlm_ldap_client_get_attrs(char const **values, int *idx, CONF_SECTION
        return 0;
 }
 
-/** Iterate over pairs in mapping section creating equivalent client pairs from LDAP values
- *
- * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too.
- *
- * @param[in] inst rlm_ldap configuration.
- * @param[out] client config section.
- * @param[in] map section.
- * @param[in] conn LDAP connection.
- * @param[in] entry returned from search.
- * @return 0 on success else -1 on error.
- */
-static int rlm_ldap_client_map_section(ldap_instance_t const *inst, CONF_SECTION *client,
-                                      CONF_SECTION const *map, ldap_handle_t *conn,
-                                      LDAPMessage *entry)
-{
-       CONF_ITEM const *ci;
-
-       for (ci = cf_item_find_next(map, NULL);
-            ci != NULL;
-            ci = cf_item_find_next(map, ci)) {
-               CONF_PAIR const *cp;
-               struct berval **values;
-               char *value;
-               char const *attr;
-
-               /*
-                *      Recursively process map subsection
-                */
-               if (cf_item_is_section(ci)) {
-                       CONF_SECTION *cs, *cc;
-
-                       cs = cf_item_to_section(ci);
-                       cc = cf_section_alloc(client, cf_section_name1(cs), cf_section_name2(cs));
-                       if (!cc) return -1;
-
-                       cf_section_add(client, cc);
-
-                       if (rlm_ldap_client_map_section(inst, cc, cs, conn, entry) < 0) return -1;
-                       continue;
-               }
+typedef struct ldap_client_data {
+       ldap_handle_t *conn;
+       LDAPMessage *entry;
+} ldap_client_data_t;
 
-               cp = cf_item_to_pair(ci);
-               attr = cf_pair_attr(cp);
-
-               values = ldap_get_values_len(conn->handle, entry, cf_pair_value(cp));
-               if (!values) continue;
+static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
+{
+       struct berval **values;
+       ldap_client_data_t *this = data;
 
-               value = rlm_ldap_berval_to_string(NULL, values[0]);
-               cp = cf_pair_alloc(client, attr, value, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
-               if (!cp) {
-                       LDAP_ERR("Failed allocing pair \"%s\" = \"%s\"", attr, value);
-                       talloc_free(value);
-                       return -1;
-               }
-               talloc_free(value);
-               ldap_value_free_len(values);
-               cf_item_add(client, cf_pair_to_item(cp));
+       values = ldap_get_values_len(this->conn->handle, this->entry, cf_pair_value(cp));
+       if (!values) {
+               *out = NULL;
+               return 0;
        }
 
+       *out = rlm_ldap_berval_to_string(NULL, values[0]);
+       ldap_value_free_len(values);
+
+       if (!*out) return -1;
        return 0;
 }
 
 /** Load clients from LDAP on server start
  *
  * @param[in] inst rlm_ldap configuration.
- * @param[in] cs to load client attribute/LDAP attribute mappings from.
- * @return -1 on error else 0.
+ * @param[in] tmpl to use as the base for the new client.
+ * @param[in] map to load client attribute/LDAP attribute mappings from.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
  */
-int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
+int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTION *map)
 {
        int             ret = 0;
        ldap_rcode_t    status;
@@ -154,30 +120,30 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
 
        rad_assert(inst->clientobj_base_dn);
 
-       if (!inst->clientobj_filter) {
-               LDAP_ERR("Told to load clients but 'client.filter' not specified");
-
-               return -1;
-       }
-
-       count = cf_pair_count(cs);
+       count = cf_pair_count(map);
        count++;
 
        /*
         *      Create an array of LDAP attributes to feed to rlm_ldap_search.
         */
        attrs = talloc_array(inst, char const *, count);
-       if (rlm_ldap_client_get_attrs(attrs, &idx, cs) < 0) return -1;
+       if (rlm_ldap_client_get_attrs(attrs, &idx, map) < 0) {
+               talloc_free(attrs);
+               return -1;
+       }
 
        conn = mod_conn_get(inst, NULL);
-       if (!conn) return -1;
+       if (!conn) {
+               talloc_free(attrs);
+               return -1;
+       }
 
        /*
         *      Perform all searches as the admin user.
         */
        if (conn->rebound) {
-               status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password,
-                                      conn->inst->admin_sasl_mech, true);
+               status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_identity, conn->inst->admin_password,
+                                      &(conn->inst->admin_sasl), true);
                if (status != LDAP_PROC_SUCCESS) {
                        ret = -1;
                        goto finish;
@@ -188,8 +154,8 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
                conn->rebound = false;
        }
 
-       status = rlm_ldap_search(inst, NULL, &conn, inst->clientobj_base_dn, inst->clientobj_scope,
-                                inst->clientobj_filter, attrs, &result);
+       status = rlm_ldap_search(&result, inst, NULL, &conn, inst->clientobj_base_dn, inst->clientobj_scope,
+                                inst->clientobj_filter, attrs, NULL, NULL);
        switch (status) {
        case LDAP_PROC_SUCCESS:
                break;
@@ -217,11 +183,12 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
        }
 
        do {
-               CONF_SECTION *cc;
-               char *id;
+               ldap_client_data_t      data;
 
-               struct berval **values;
-               char *value = NULL;
+               CONF_SECTION            *client;
+               char                    *id;
+
+               struct berval           **values;
 
                id = dn = ldap_get_dn(conn->handle, entry);
                if (!dn) {
@@ -234,7 +201,7 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
                }
                rlm_ldap_normalise_dn(dn, dn);
 
-               cp = cf_pair_find(cs, "identifier");
+               cp = cf_pair_find(map, "identifier");
                if (cp) {
                        values = ldap_get_values_len(conn->handle, entry, cf_pair_value(cp));
                        if (values) id = rlm_ldap_berval_to_string(NULL, values[0]);
@@ -244,11 +211,14 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
                /*
                 *      Iterate over mapping sections
                 */
-               cc = cf_section_alloc(NULL, "client", id);
-               talloc_free(value);
+               client = tmpl ? cf_section_dup(NULL, tmpl, "client", id, true) :
+                               cf_section_alloc(NULL, "client", id);
+
+               data.conn = conn;
+               data.entry = entry;
 
-               if (rlm_ldap_client_map_section(inst, cc, cs, conn, entry) < 0) {
-                       talloc_free(cc);
+               if (client_map_section(client, map, _get_client_value, &data) < 0) {
+                       talloc_free(client);
                        ret = -1;
                        goto finish;
                }
@@ -256,9 +226,9 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
                /*
                 *@todo these should be parented from something
                 */
-               c = client_afrom_cs(NULL, cc, false, false);
+               c = client_afrom_cs(NULL, client, false, false);
                if (!c) {
-                       talloc_free(cc);
+                       talloc_free(client);
                        ret = -1;
                        goto finish;
                }
@@ -266,7 +236,7 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
                /*
                 *      Client parents the CONF_SECTION which defined it
                 */
-               talloc_steal(c, cc);
+               talloc_steal(c, client);
 
                if (!client_add(NULL, c)) {
                        LDAP_ERR("Failed to add client \"%s\", possible duplicate?", dn);
index 7cee66b..a3ca36f 100644 (file)
@@ -1,10 +1,22 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
+/* Define to 1 if you have the `ldap_create_sort_control' function. */
+#undef HAVE_LDAP_CREATE_SORT_CONTROL
+
+/* Define to 1 if you have the `ldap_create_sort_keylist' function. */
+#undef HAVE_LDAP_CREATE_SORT_KEYLIST
+
+/* Define to 1 if you have the `ldap_free_sort_keylist' function. */
+#undef HAVE_LDAP_FREE_SORT_KEYLIST
+
 /* Define to 1 if you have the `ldap_initialize' function. */
 #undef HAVE_LDAP_INITIALIZE
 
-/* Define to 1 if you have the `ldap_sasl_bind' function. */
-#undef HAVE_LDAP_SASL_BIND
+/* Define to 1 if you have the `ldap_is_ldap_url' function. */
+#undef HAVE_LDAP_IS_LDAP_URL
+
+/* Define to 1 if you have the `ldap_sasl_interactive_bind' function. */
+#undef HAVE_LDAP_SASL_INTERACTIVE_BIND
 
 /* Define to 1 if you have the `ldap_set_rebind_proc' function. */
 #undef HAVE_LDAP_SET_REBIND_PROC
 /* Define to 1 if you have the `ldap_unbind_ext_s' function. */
 #undef HAVE_LDAP_UNBIND_EXT_S
 
+/* Define to 1 if you have the `ldap_url_desc2str' function. */
+#undef HAVE_LDAP_URL_DESC2STR
+
+/* Define to 1 if you have the `ldap_url_parse' function. */
+#undef HAVE_LDAP_URL_PARSE
+
 /* Number of arguments the rebind procedure takes */
 #undef LDAP_SET_REBIND_PROC_ARGS
 
@@ -38,3 +56,6 @@
 
 /* Build the server with support for Novell eDir Universal Password */
 #undef WITH_EDIR
+
+/* Build the server with support for SASL binds */
+#undef WITH_SASL
index d34edfa..940411e 100755 (executable)
@@ -586,6 +586,7 @@ ac_unique_file="rlm_ldap.c"
 ac_subst_vars='LTLIBOBJS
 LIBOBJS
 targetname
+SASL
 mod_cflags
 mod_ldflags
 OBJEXT
@@ -1853,6 +1854,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 fail=
 SMART_LIBS=
 SMART_CLFAGS=
+SASL=
 if test x$with_rlm_ldap != xno; then
 
                                ac_ext=c
@@ -3097,11 +3099,17 @@ smart_prefix=
 
 
        if test "x$fail" = "x"; then
-               for ac_func in ldap_sasl_bind \
+               for ac_func in ldap_sasl_interactive_bind \
                        ldap_unbind_ext_s \
                        ldap_start_tls_s \
                        ldap_initialize \
                        ldap_set_rebind_proc \
+                       ldap_create_sort_control \
+                       ldap_create_sort_keylist \
+                       ldap_free_sort_keylist \
+                       ldap_url_parse \
+                       ldap_is_ldap_url \
+                       ldap_url_desc2str
 
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
@@ -3145,6 +3153,241 @@ fi
 $as_echo "$ac_cv_ldap_set_rebind_proc" >&6; }
        fi
 
+
+
+ac_safe=`echo "sasl/sasl.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir="/usr/local/include /opt/include"
+
+_smart_try_dir=
+_smart_include_dir=
+
+for _prefix in $smart_prefix ""; do
+  for _dir in $smart_try_dir; do
+    _smart_try_dir="${_smart_try_dir} ${_dir}/${_prefix}"
+  done
+
+  for _dir in $smart_include_dir; do
+    _smart_include_dir="${_smart_include_dir} ${_dir}/${_prefix}"
+  done
+done
+
+if test "x$_smart_try_dir" != "x"; then
+  for try in $_smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sasl/sasl.h in $try" >&5
+$as_echo_n "checking for sasl/sasl.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <sasl/sasl.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
+  for _prefix in $smart_prefix; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/sasl/sasl.h" >&5
+$as_echo_n "checking for ${_prefix}/sasl/sasl.h... " >&6; }
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <sasl/sasl.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem ${_prefix}/"
+                    { $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
+fi
+
+if test "x$smart_include" = "x"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sasl/sasl.h" >&5
+$as_echo_n "checking for sasl/sasl.h... " >&6; }
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <sasl/sasl.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
+
+  for prefix in $smart_prefix; do
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file="${_prefix}/${1}"
+
+  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\""
+
+  done
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=sasl/sasl.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; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sasl/sasl.h in $try" >&5
+$as_echo_n "checking for sasl/sasl.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <sasl/sasl.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
+
+smart_prefix=
+
+       if test "x$ac_cv_header_sasl_sasl_h" = "xyes"; then
+         if test x"$ac_cv_func_ldap_sasl_interactive_bind" = "xyes"; then
+
+$as_echo "#define WITH_SASL 1" >>confdefs.h
+
+           SASL=sasl.c
+          fi
+       fi
+
        targetname=rlm_ldap
 else
        targetname=
@@ -3185,6 +3428,8 @@ mod_cflags="$SMART_CPPFLAGS"
 
 
 
+
+
 ac_config_headers="$ac_config_headers config.h"
 
 
index a0dd114..b014b70 100644 (file)
@@ -6,6 +6,7 @@ AC_DEFUN(modname,[rlm_ldap])
 fail=
 SMART_LIBS=
 SMART_CLFAGS=
+SASL=
 if test x$with_[]modname != xno; then
 
        dnl ############################################################
@@ -87,11 +88,17 @@ if test x$with_[]modname != xno; then
 
        if test "x$fail" = "x"; then
                AC_CHECK_FUNCS(
-                       ldap_sasl_bind \
+                       ldap_sasl_interactive_bind \
                        ldap_unbind_ext_s \
                        ldap_start_tls_s \
                        ldap_initialize \
                        ldap_set_rebind_proc \
+                       ldap_create_sort_control \
+                       ldap_create_sort_keylist \
+                       ldap_free_sort_keylist \
+                       ldap_url_parse \
+                       ldap_is_ldap_url \
+                       ldap_url_desc2str
                )
                AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, ac_cv_ldap_set_rebind_proc, [
                        AC_TRY_COMPILE([
@@ -102,6 +109,17 @@ if test x$with_[]modname != xno; then
                ])
        fi
 
+       dnl ############################################################
+       dnl # Check for SASL support
+       dnl ############################################################
+       FR_SMART_CHECK_INCLUDE([sasl/sasl.h])
+       if test "x$ac_cv_header_sasl_sasl_h" = "xyes"; then
+         if test x"$ac_cv_func_ldap_sasl_interactive_bind" = "xyes"; then
+           AC_DEFINE(WITH_SASL, 1, [Build the server with support for SASL binds])
+           SASL=sasl.c
+         fi
+       fi
+
        targetname=modname
 else
        targetname=
@@ -129,8 +147,10 @@ AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, ${ac_cv_ldap_set_rebind_proc}, [Nu
 
 mod_ldflags=$SMART_LIBS
 mod_cflags="$SMART_CPPFLAGS"
+
 AC_SUBST(mod_ldflags)
 AC_SUBST(mod_cflags)
+AC_SUBST(SASL)
 AC_SUBST(targetname)
 AC_CONFIG_HEADER(config.h)
 AC_OUTPUT(all.mk)
index 3f720f7..ddac7e2 100644 (file)
@@ -63,7 +63,9 @@ RCSID("$Id$")
  *
  * @param[out] request_bv where to write the request BER value (must be freed with ber_bvfree).
  * @param[in] dn to query for.
- * @return 0 on success, and < 0 on error.
+ * @return
+ *     - 0 on success.
+ *     - < 0 on error.
  */
 static int ber_encode_request_data(char const *dn, struct berval **request_bv)
 {
@@ -119,7 +121,9 @@ finish:
  * @param[out] server_version that responded.
  * @param[out] out data.
  * @param[out] outlen Length of data written to out.
- * @return 0 on success, and < 0 on error.
+ * @return
+ *     - 0 on success.
+ *     - < 0 on error.
  */
 static int ber_decode_login_data(struct berval *reply_bv, int *server_version, void *out, size_t *outlen)
 {
@@ -154,7 +158,9 @@ finish:
  * @param[in] dn of user we want to retrieve the password for.
  * @param[out] password Where to write the retrieved password.
  * @param[out] passlen Length of data written to the password buffer.
- * @return 0 on success and < 0 on failure.
+ * @return
+ *     - 0 on success.
+ *     - < 0 on failure.
  */
 int nmasldap_get_password(LDAP *ld, char const *dn, char *password, size_t *passlen)
 {
index a882078..12f34da 100644 (file)
@@ -20,8 +20,9 @@
  * @brief LDAP module group functions.
  *
  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ *
  * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
- * @copyright 2013 The FreeRADIUS Server Project.
+ * @copyright 2013-2015 The FreeRADIUS Server Project.
  */
 #include       <freeradius-devel/rad_assert.h>
 #include       <ctype.h>
@@ -41,7 +42,7 @@
  * @param[in] outlen Size of out.
  * @return One of the RLM_MODULE_* values.
  */
-static rlm_rcode_t rlm_ldap_group_name2dn(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+static rlm_rcode_t rlm_ldap_group_name2dn(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                                          char **names, char **out, size_t outlen)
 {
        rlm_rcode_t rcode = RLM_MODULE_OK;
@@ -101,8 +102,8 @@ static rlm_rcode_t rlm_ldap_group_name2dn(ldap_instance_t const *inst, REQUEST *
                return RLM_MODULE_INVALID;
        }
 
-       status = rlm_ldap_search(inst, request, pconn, base_dn, inst->groupobj_scope,
-                                filter, attrs, &result);
+       status = rlm_ldap_search(&result, inst, request, pconn, base_dn, inst->groupobj_scope,
+                                filter, attrs, NULL, NULL);
        switch (status) {
        case LDAP_PROC_SUCCESS:
                break;
@@ -192,7 +193,7 @@ finish:
  * @param[out] out Where to write group name (must be freed with talloc_free).
  * @return One of the RLM_MODULE_* values.
  */
-static rlm_rcode_t rlm_ldap_group_dn2name(ldap_instance_t const *inst, REQUEST *request,
+static rlm_rcode_t rlm_ldap_group_dn2name(rlm_ldap_t const *inst, REQUEST *request,
                                          ldap_handle_t **pconn, char const *dn, char **out)
 {
        rlm_rcode_t rcode = RLM_MODULE_OK;
@@ -213,7 +214,7 @@ static rlm_rcode_t rlm_ldap_group_dn2name(ldap_instance_t const *inst, REQUEST *
 
        RDEBUG("Resolving group DN \"%s\" to group name", dn);
 
-       status = rlm_ldap_search(inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, &result);
+       status = rlm_ldap_search(&result, inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, NULL, NULL);
        switch (status) {
        case LDAP_PROC_SUCCESS:
                break;
@@ -263,7 +264,7 @@ finish:
  * @param[in] attr membership attribute to look for in the entry.
  * @return One of the RLM_MODULE_* values.
  */
-rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                                       LDAPMessage *entry, char const *attr)
 {
        rlm_rcode_t rcode = RLM_MODULE_OK;
@@ -311,7 +312,7 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
         *      once all group info has been gathered/resolved
         *      successfully.
         */
-       fr_cursor_init(&groups_cursor, groups);
+       fr_cursor_init(&groups_cursor, &groups);
 
        for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) {
                is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len);
@@ -321,8 +322,8 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
                         *      The easy case, we're caching DNs and we got a DN.
                         */
                        if (is_dn) {
-                               MEM(vp = pairalloc(list_ctx, inst->cache_da));
-                               pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
+                               MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
+                               fr_pair_value_bstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
                                fr_cursor_insert(&groups_cursor, vp);
                        /*
                         *      We were told to cache DNs but we got a name, we now need to resolve
@@ -338,8 +339,8 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
                         *      The easy case, we're caching names and we got a name.
                         */
                        if (!is_dn) {
-                               MEM(vp = pairalloc(list_ctx, inst->cache_da));
-                               pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
+                               MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
+                               fr_pair_value_bstrncpy(vp, values[i]->bv_val, values[i]->bv_len);
                                fr_cursor_insert(&groups_cursor, vp);
                        /*
                         *      We were told to cache names but we got a DN, we now need to resolve
@@ -356,13 +357,13 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
                                if (rcode != RLM_MODULE_OK) {
                                        ldap_value_free_len(values);
                                        talloc_free(value_ctx);
-                                       pairfree(&groups);
+                                       fr_pair_list_free(&groups);
 
                                        return rcode;
                                }
 
-                               MEM(vp = pairalloc(list_ctx, inst->cache_da));
-                               pairstrncpy(vp, name, talloc_array_length(name) - 1);
+                               MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
+                               fr_pair_value_bstrncpy(vp, name, talloc_array_length(name) - 1);
                                fr_cursor_insert(&groups_cursor, vp);
                                talloc_free(name);
                        }
@@ -379,7 +380,7 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
 
        fr_cursor_init(&list_cursor, list);
 
-       RDEBUG("Adding cacheable group memberships");
+       RDEBUG("Adding cacheable user object memberships");
        RINDENT();
        if (RDEBUG_ENABLED) {
                for (vp = fr_cursor_first(&groups_cursor);
@@ -392,8 +393,8 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
        fr_cursor_merge(&list_cursor, groups);
 
        for (dn_p = group_dn; *dn_p; dn_p++) {
-               MEM(vp = pairalloc(list_ctx, inst->cache_da));
-               pairstrcpy(vp, *dn_p);
+               MEM(vp = fr_pair_afrom_da(list_ctx, inst->cache_da));
+               fr_pair_value_strcpy(vp, *dn_p);
                fr_cursor_insert(&list_cursor, vp);
 
                RDEBUG("&control:%s += \"%s\"", inst->cache_da->name, vp->vp_strvalue);
@@ -411,7 +412,7 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
  * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
  * @return One of the RLM_MODULE_* values.
  */
-rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn)
+rlm_rcode_t rlm_ldap_cacheable_groupobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn)
 {
        rlm_rcode_t rcode = RLM_MODULE_OK;
        ldap_rcode_t status;
@@ -452,7 +453,8 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *re
                return RLM_MODULE_INVALID;
        }
 
-       status = rlm_ldap_search(inst, request, pconn, base_dn, inst->groupobj_scope, filter, attrs, &result);
+       status = rlm_ldap_search(&result, inst, request, pconn, base_dn,
+                                inst->groupobj_scope, filter, attrs, NULL, NULL);
        switch (status) {
        case LDAP_PROC_SUCCESS:
                break;
@@ -472,6 +474,7 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *re
                goto finish;
        }
 
+       RDEBUG("Adding cacheable group object memberships");
        do {
                if (inst->cacheable_group_dn) {
                        dn = ldap_get_dn((*pconn)->handle, entry);
@@ -483,10 +486,12 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *re
                        }
                        rlm_ldap_normalise_dn(dn, dn);
 
-                       MEM(vp = pairmake_config(inst->cache_da->name, NULL, T_OP_ADD));
-                       pairstrcpy(vp, dn);
+                       MEM(vp = pair_make_config(inst->cache_da->name, NULL, T_OP_ADD));
+                       fr_pair_value_strcpy(vp, dn);
 
-                       RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, dn);
+                       RINDENT();
+                       RDEBUG("&control:%s += \"%s\"", inst->cache_da->name, dn);
+                       REXDENT();
                        ldap_memfree(dn);
                }
 
@@ -496,11 +501,13 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *re
                        values = ldap_get_values_len((*pconn)->handle, entry, inst->groupobj_name_attr);
                        if (!values) continue;
 
-                       MEM(vp = pairmake_config(inst->cache_da->name, NULL, T_OP_ADD));
-                       pairstrncpy(vp, values[0]->bv_val, values[0]->bv_len);
+                       MEM(vp = pair_make_config(inst->cache_da->name, NULL, T_OP_ADD));
+                       fr_pair_value_bstrncpy(vp, values[0]->bv_val, values[0]->bv_len);
 
-                       RDEBUG("Added control:%s with value \"%.*s\"", inst->cache_da->name,
+                       RINDENT();
+                       RDEBUG("&control:%s += \"%.*s\"", inst->cache_da->name,
                               (int)values[0]->bv_len, values[0]->bv_val);
+                       REXDENT();
 
                        ldap_value_free_len(values);
                }
@@ -520,7 +527,7 @@ finish:
  * @param[in] check vp containing the group value (name or dn).
  * @return One of the RLM_MODULE_* values.
  */
-rlm_rcode_t rlm_ldap_check_groupobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+rlm_rcode_t rlm_ldap_check_groupobj_dynamic(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                                            VALUE_PAIR *check)
 
 {
@@ -596,7 +603,7 @@ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(ldap_instance_t const *inst, REQUEST
        }
 
        RINDENT();
-       status = rlm_ldap_search(inst, request, pconn, base_dn, inst->groupobj_scope, filter, NULL, NULL);
+       status = rlm_ldap_search(NULL, inst, request, pconn, base_dn, inst->groupobj_scope, filter, NULL, NULL, NULL);
        REXDENT();
        switch (status) {
        case LDAP_PROC_SUCCESS:
@@ -622,7 +629,7 @@ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(ldap_instance_t const *inst, REQUEST
  * @param[in] check vp containing the group value (name or dn).
  * @return One of the RLM_MODULE_* values.
  */
-rlm_rcode_t rlm_ldap_check_userobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+rlm_rcode_t rlm_ldap_check_userobj_dynamic(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                                           char const *dn, VALUE_PAIR *check)
 {
        rlm_rcode_t     rcode = RLM_MODULE_NOTFOUND, ret;
@@ -638,7 +645,7 @@ rlm_rcode_t rlm_ldap_check_userobj_dynamic(ldap_instance_t const *inst, REQUEST
 
        RDEBUG2("Checking user object's %s attributes", inst->userobj_membership_attr);
        RINDENT();
-       status = rlm_ldap_search(inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, &result);
+       status = rlm_ldap_search(&result, inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, NULL, NULL);
        REXDENT();
        switch (status) {
        case LDAP_PROC_SUCCESS:
@@ -802,13 +809,13 @@ finish:
  *
  * @return One of the RLM_MODULE_* values.
  */
-rlm_rcode_t rlm_ldap_check_cached(ldap_instance_t const *inst, REQUEST *request, VALUE_PAIR *check)
+rlm_rcode_t rlm_ldap_check_cached(rlm_ldap_t const *inst, REQUEST *request, VALUE_PAIR *check)
 {
        VALUE_PAIR      *vp;
        int             ret;
        vp_cursor_t     cursor;
 
-       fr_cursor_init(&cursor, &request->config_items);
+       fr_cursor_init(&cursor, &request->config);
 
        /*
         *      We return RLM_MODULE_INVALID here as an indication
@@ -819,7 +826,7 @@ rlm_rcode_t rlm_ldap_check_cached(ldap_instance_t const *inst, REQUEST *request,
        fr_cursor_first(&cursor);
 
        while ((vp = fr_cursor_next_by_num(&cursor, inst->cache_da->attr, inst->cache_da->vendor, TAG_ANY))) {
-               ret = paircmp_op(T_OP_CMP_EQ, vp, check);
+               ret = fr_pair_cmp_op(T_OP_CMP_EQ, vp, check);
                if (ret == 1) {
                        RDEBUG2("User found. Matched cached membership");
                        return RLM_MODULE_OK;
index 8ac2de6..cadfc60 100644 (file)
@@ -20,8 +20,9 @@
  * @brief LDAP module library functions.
  *
  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
- * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
- * @copyright 2013 The FreeRADIUS Server Project.
+ * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013-2015 Network RADIUS SARL <info@networkradius.com>
+ * @copyright 2013-2015 The FreeRADIUS Server Project.
  */
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
 
 /** Converts "bad" strings into ones which are safe for LDAP
  *
- * This is a callback for xlat operations.
+ * @note RFC 4515 says filter strings can only use the @verbatim \<hex><hex> @endverbatim
+ *     format, whereas RFC 4514 indicates that some chars in DNs, may be escaped simply
+ *     with a backslash. For simplicity, we always use the hex escape sequences.
+ *     In other areas where we're doing DN comparison, the DNs need to be normalised first
+ *     so that they both use only hex escape sequences.
  *
- * Will escape any characters in input strings that would cause the string to be interpreted as part of a DN and or
- * filter. Escape sequence is @verbatim \<hex><hex> @endverbatim
+ * @note This is a callback for xlat operations.
+ *
+ * Will escape any characters in input strings that would cause the string to be interpreted
+ * as part of a DN and or filter. Escape sequence is @verbatim \<hex><hex> @endverbatim.
  *
  * @param request The current request.
  * @param out Pointer to output buffer.
@@ -51,17 +58,14 @@ size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, c
        static char const hextab[] = "0123456789abcdef";
        size_t left = outlen;
 
-       if (*in && ((*in == ' ') || (*in == '#'))) {
-               goto encode;
-       }
+       if (*in && ((*in == ' ') || (*in == '#'))) goto encode;
 
        while (*in) {
                /*
                 *      Encode unsafe characters.
                 */
                if (memchr(encode, *in, sizeof(encode) - 1)) {
-                       encode:
-
+               encode:
                        /*
                         *      Only 3 or less bytes available.
                         */
@@ -94,7 +98,9 @@ size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, c
  *
  * @param[in] in Str to check.
  * @param[in] inlen Length of string to check.
- * @return true if string looks like a DN, else false.
+ * @return
+ *     - true if string looks like a DN.
+ *     - false if string does not look like DN.
  */
 bool rlm_ldap_is_dn(char const *in, size_t inlen)
 {
@@ -299,7 +305,9 @@ size_t rlm_ldap_normalise_dn(char *out, char const *in)
  *
  * @param full DN.
  * @param part Partial DN as returned by ldap_parse_result.
- * @return the length of the portion of full which wasn't matched or -1 on error.
+ * @return
+ *     - Length of the portion of full which wasn't matched
+ *     - -1 on failure.
  */
 static size_t rlm_ldap_common_dn(char const *full, char const *part)
 {
@@ -435,16 +443,16 @@ char const *rlm_ldap_error_str(ldap_handle_t const *conn)
  *
  * @param[in] inst of LDAP module.
  * @param[in] conn Current connection.
- * @param[in] msgid returned from last operation.
+ * @param[in] msgid returned from last operation. May be -1 if no result processing is required.
  * @param[in] dn Last search or bind DN.
  * @param[out] result Where to write result, if NULL result will be freed.
  * @param[out] error Where to write the error string, may be NULL, must not be freed.
  * @param[out] extra Where to write additional error string to, may be NULL (faster) or must be freed
  *     (with talloc_free).
- * @return One of the LDAP_PROC_* codes.
+ * @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
  */
-static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn,
-                                   LDAPMessage **result, char const **error, char **extra)
+ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn,
+                            LDAPMessage **result, char const **error, char **extra)
 {
        ldap_rcode_t status = LDAP_PROC_SUCCESS;
 
@@ -461,18 +469,15 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
 
        struct timeval tv;              // Holds timeout values.
 
-       LDAPMessage *tmp_msg;           // Temporary message pointer storage if we weren't provided with one.
+       LDAPMessage *tmp_msg = NULL;    // Temporary message pointer storage if we weren't provided with one.
 
        char const *tmp_err;            // Temporary error pointer storage if we weren't provided with one.
 
-       if (!error) {
-               error = &tmp_err;
-       }
+       if (!error) error = &tmp_err;
        *error = NULL;
 
-       if (extra) {
-               *extra = NULL;
-       }
+       if (extra) *extra = NULL;
+       if (result) *result = NULL;
 
        /*
         *      We always need the result, but our caller may not
@@ -482,20 +487,19 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
                freeit = true;
        }
 
-       *result = NULL;
-
        /*
         *      Check if there was an error sending the request
         */
        ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &lib_errno);
        if (lib_errno != LDAP_SUCCESS) goto process_error;
+       if (msgid < 0) return LDAP_SUCCESS;     /* No msgid and no error, return now */
 
        memset(&tv, 0, sizeof(tv));
        tv.tv_sec = inst->res_timeout;
 
        /*
         *      Now retrieve the result and check for errors
-        *      ldap_result returns -1 on error, and 0 on timeout
+        *      ldap_result returns -1 on failure, and 0 on timeout
         */
        lib_errno = ldap_result(conn->handle, msgid, 1, &tv, result);
        if (lib_errno == 0) {
@@ -537,6 +541,11 @@ process_error:
                *error = "Success";
                break;
 
+       case LDAP_SASL_BIND_IN_PROGRESS:
+               *error = "Continuing";
+               status = LDAP_PROC_CONTINUE;
+               break;
+
        case LDAP_NO_SUCH_OBJECT:
                *error = "The specified DN wasn't found";
                status = LDAP_PROC_BAD_DN;
@@ -652,7 +661,7 @@ process_error:
 
        talloc_free(our_err);
 
-       if ((lib_errno || srv_errno) && *result) {
+       if ((status < 0) && *result) {
                ldap_msgfree(*result);
                *result = NULL;
        }
@@ -669,25 +678,29 @@ process_error:
  * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
  * @param[in] dn of the user, may be NULL to bind anonymously.
  * @param[in] password of the user, may be NULL if no password is specified.
- * @param[in] sasl_mech SASL mechanism to use for bind.
+ * @param[in] sasl mechanism to use for bind, and additional parameters.
  * @param[in] retry if the server is down.
- * @return one of the LDAP_PROC_* values.
+ * @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
  */
-ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
-                          char const *password, char const *sasl_mech, bool retry)
+ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
+                          char const *password, ldap_sasl *sasl, bool retry)
 {
-       ldap_rcode_t    status = LDAP_PROC_ERROR;
+       ldap_rcode_t            status = LDAP_PROC_ERROR;
 
-       int             msgid = -1;
+       int                     msgid = -1;
 
-       char const      *error = NULL;
-       char            *extra = NULL;
+       char const              *error = NULL;
+       char                    *extra = NULL;
 
-       int             i, num;
+       int                     i, num;
 
        rad_assert(*pconn && (*pconn)->handle);
        rad_assert(!retry || inst->pool);
 
+#ifndef WITH_SASL
+       rad_assert(!sasl->mech);
+#endif
+
        /*
         *      Bind as anonymous user
         */
@@ -697,33 +710,28 @@ ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_h
         *      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;
+       num = retry ? fr_connection_pool_get_num(inst->pool) : 0;
        for (i = num; i >= 0; i--) {
-#ifdef HAVE_LDAP_SASL_BIND
-               if (sasl_mech) {
-                       struct berval cred;
-
-                       if (password) {
-                               memcpy(&cred.bv_val, &password, sizeof(cred.bv_val));
-                               cred.bv_len = talloc_array_length(password) - 1;
-                       } else {
-                               memset(&cred, 0, sizeof(cred));
-                       }
-                       ldap_sasl_bind((*pconn)->handle, dn, sasl_mech, &cred, NULL, NULL, &msgid);
+#ifdef WITH_SASL
+               if (sasl && sasl->mech) {
+                       status = rlm_ldap_sasl_interactive(inst, request, *pconn, dn, password, sasl,
+                                                          &error, &extra);
                } else
 #endif
-               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);
+               {
+                       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->name);
+                               }
                        }
-               }
 
-               status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+                       status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+               }
 
                switch (status) {
                case LDAP_PROC_SUCCESS:
@@ -786,6 +794,8 @@ ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_h
  *
  * Binds as the administrative user and performs a search, dealing with any errors.
  *
+ * @param[out] result Where to store the result. Must be freed with ldap_msgfree if LDAP_PROC_SUCCESS is returned.
+ *     May be NULL in which case result will be automatically freed after use.
  * @param[in] inst rlm_ldap configuration.
  * @param[in] request Current request.
  * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
@@ -793,13 +803,14 @@ ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_h
  * @param[in] scope to use (LDAP_SCOPE_BASE, LDAP_SCOPE_ONE, LDAP_SCOPE_SUB).
  * @param[in] filter to use, should be pre-escaped.
  * @param[in] attrs to retrieve.
- * @param[out] result Where to store the result. Must be freed with ldap_msgfree if LDAP_PROC_SUCCESS is returned.
- *     May be NULL in which case result will be automatically freed after use.
- * @return One of the LDAP_PROC_* values.
+ * @param[in] serverctrls Search controls to pass to the server.  May be NULL.
+ * @param[in] clientctrls Search controls for ldap_search.  May be NULL.
+ * @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
  */
-ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+ldap_rcode_t rlm_ldap_search(LDAPMessage **result, rlm_ldap_t const *inst, REQUEST *request,
+                            ldap_handle_t **pconn,
                             char const *dn, int scope, char const *filter, char const * const *attrs,
-                            LDAPMessage **result)
+                            LDAPControl **serverctrls, LDAPControl **clientctrls)
 {
        ldap_rcode_t    status = LDAP_PROC_ERROR;
        LDAPMessage     *our_result = NULL;
@@ -816,7 +827,6 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
 
        int             i;
 
-
        rad_assert(*pconn && (*pconn)->handle);
 
        /*
@@ -830,8 +840,8 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
         *      Do all searches as the admin user.
         */
        if ((*pconn)->rebound) {
-               status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_dn, (*pconn)->inst->password,
-                                      (*pconn)->inst->admin_sasl_mech, true);
+               status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_identity,
+                                      (*pconn)->inst->admin_password, &(*pconn)->inst->admin_sasl, true);
                if (status != LDAP_PROC_SUCCESS) {
                        return LDAP_PROC_ERROR;
                }
@@ -860,9 +870,9 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
         *      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--) {
+       for (i = fr_connection_pool_get_num(inst->pool); i >= 0; i--) {
                (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs,
-                                      0, NULL, NULL, &tv, 0, &msgid);
+                                      0, serverctrls, clientctrls, &tv, 0, &msgid);
 
                LDAP_DBG_REQ("Waiting for search result...");
                status = rlm_ldap_result(inst, *pconn, msgid, dn, &our_result, &error, &extra);
@@ -935,9 +945,7 @@ finish:
         *      it to where our caller said.
         */
        if (!result) {
-               if (our_result) {
-                       ldap_msgfree(our_result);
-               }
+               if (our_result) ldap_msgfree(our_result);
        } else {
                *result = our_result;
        }
@@ -954,9 +962,9 @@ finish:
  * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
  * @param[in] dn of the object to modify.
  * @param[in] mods to make, see 'man ldap_modify' for more information.
- * @return One of the LDAP_PROC_* values.
+ * @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
  */
-ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+ldap_rcode_t rlm_ldap_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                             char const *dn, LDAPMod *mods[])
 {
        ldap_rcode_t    status = LDAP_PROC_ERROR;
@@ -974,8 +982,8 @@ ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap
         *      Perform all modifications as the admin user.
         */
        if ((*pconn)->rebound) {
-               status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_dn, (*pconn)->inst->password,
-                                      (*pconn)->inst->admin_sasl_mech, true);
+               status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_identity,
+                                      (*pconn)->inst->admin_password, &(*pconn)->inst->admin_sasl, true);
                if (status != LDAP_PROC_SUCCESS) {
                        return LDAP_PROC_ERROR;
                }
@@ -989,7 +997,7 @@ ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap
         *      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--) {
+       for (i = fr_connection_pool_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);
 
@@ -1005,7 +1013,6 @@ ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap
                                RWDEBUG("Modify failed: %s. Got new socket, retrying...", error);
 
                                talloc_free(extra); /* don't leak debug info */
-
                                continue;
                        }
 
@@ -1035,11 +1042,12 @@ finish:
 
 /** Retrieve the DN of a user object
  *
- * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any attributes
- * passed and return the result in *result.
+ * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any
+ * attributes passed and return the result in *result.
  *
- * This potentially allows for all authorization and authentication checks to be performed in one ldap search
- * operation, which is a big bonus given the number of crappy, slow *cough*AD*cough* LDAP directory servers out there.
+ * This potentially allows for all authorization and authentication checks to be performed in one
+ * ldap search operation, which is a big bonus given the number of crappy, slow *cough*AD*cough*
+ * LDAP directory servers out there.
  *
  * @param[in] inst rlm_ldap configuration.
  * @param[in] request Current request.
@@ -1050,7 +1058,7 @@ finish:
  * @param[out] rcode The status of the operation, one of the RLM_MODULE_* codes.
  * @return The user's DN or NULL on error.
  */
-char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+char const *rlm_ldap_find_user(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                               char const *attrs[], bool force, LDAPMessage **result, rlm_rcode_t *rcode)
 {
        static char const *tmp_attrs[] = { NULL };
@@ -1059,11 +1067,13 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
        VALUE_PAIR      *vp = NULL;
        LDAPMessage     *tmp_msg = NULL, *entry = NULL;
        int             ldap_errno;
+       int             cnt;
        char            *dn = NULL;
        char const      *filter = NULL;
        char            filter_buff[LDAP_MAX_FILTER_STR_LEN];
        char const      *base_dn;
        char            base_dn_buff[LDAP_MAX_DN_STR_LEN];
+       LDAPControl     *serverctrls[] = { inst->userobj_sort_ctrl, NULL };
 
        bool freeit = false;                                    //!< Whether the message should
                                                                //!< be freed after being processed.
@@ -1084,7 +1094,7 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
         *      If the caller isn't looking for the result we can just return the current userdn value.
         */
        if (!force) {
-               vp = pairfind(request->config_items, PW_LDAP_USERDN, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->config, PW_LDAP_USERDN, 0, TAG_ANY);
                if (vp) {
                        RDEBUG("Using user DN from request \"%s\"", vp->vp_strvalue);
                        *rcode = RLM_MODULE_OK;
@@ -1096,8 +1106,8 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
         *      Perform all searches as the admin user.
         */
        if ((*pconn)->rebound) {
-               status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_dn, (*pconn)->inst->password,
-                                      (*pconn)->inst->admin_sasl_mech, true);
+               status = rlm_ldap_bind(inst, request, pconn, (*pconn)->inst->admin_identity,
+                                      (*pconn)->inst->admin_password, &(*pconn)->inst->admin_sasl, true);
                if (status != LDAP_PROC_SUCCESS) {
                        *rcode = RLM_MODULE_FAIL;
                        return NULL;
@@ -1126,7 +1136,8 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
                return NULL;
        }
 
-       status = rlm_ldap_search(inst, request, pconn, base_dn, inst->userobj_scope, filter, attrs, result);
+       status = rlm_ldap_search(result, inst, request, pconn, base_dn,
+                                inst->userobj_scope, filter, attrs, serverctrls, NULL);
        switch (status) {
        case LDAP_PROC_SUCCESS:
                break;
@@ -1143,6 +1154,31 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
 
        rad_assert(*pconn);
 
+       /*
+        *      Forbid the use of unsorted search results that
+        *      contain multiple entries, as it's a potential
+        *      security issue, and likely non deterministic.
+        */
+       if (!inst->userobj_sort_ctrl) {
+               cnt = ldap_count_entries((*pconn)->handle, *result);
+               if (cnt > 1) {
+                       REDEBUG("Ambiguous search result, returned %i unsorted entries (should return 1 or 0).  "
+                               "Enable sorting, or specify a more restrictive base_dn, filter or scope", cnt);
+                       REDEBUG("The following entries were returned:");
+                       RINDENT();
+                       for (entry = ldap_first_entry((*pconn)->handle, *result);
+                            entry;
+                            entry = ldap_next_entry((*pconn)->handle, entry)) {
+                               dn = ldap_get_dn((*pconn)->handle, entry);
+                               REDEBUG("%s", dn);
+                               ldap_memfree(dn);
+                       }
+                       REXDENT();
+                       *rcode = RLM_MODULE_FAIL;
+                       goto finish;
+               }
+       }
+
        entry = ldap_first_entry((*pconn)->handle, *result);
        if (!entry) {
                ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
@@ -1162,7 +1198,7 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
        rlm_ldap_normalise_dn(dn, dn);
 
        /*
-        *      We can't use pairmake here to copy the value into the
+        *      We can't use fr_pair_make here to copy the value into the
         *      attribute, as the dn must be copied into the attribute
         *      verbatim (without de-escaping).
         *
@@ -1170,9 +1206,9 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
         *      we pass the string back to libldap we must not alter it.
         */
        RDEBUG("User object found at DN \"%s\"", dn);
-       vp = pairmake(request, &request->config_items, "LDAP-UserDN", NULL, T_OP_EQ);
+       vp = fr_pair_make(request, &request->config, "LDAP-UserDN", NULL, T_OP_EQ);
        if (vp) {
-               pairstrcpy(vp, dn);
+               fr_pair_value_strcpy(vp, dn);
                *rcode = RLM_MODULE_OK;
        }
 
@@ -1193,9 +1229,11 @@ finish:
  * @param[in] request Current request.
  * @param[in] conn used to retrieve access attributes.
  * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search.
- * @return RLM_MODULE_USERLOCK if the user was denied access, else RLM_MODULE_OK.
+ * @return
+ *     - #RLM_MODULE_USERLOCK if the user was denied access.
+ *     - #RLM_MODULE_OK otherwise.
  */
-rlm_rcode_t rlm_ldap_check_access(ldap_instance_t const *inst, REQUEST *request,
+rlm_rcode_t rlm_ldap_check_access(rlm_ldap_t const *inst, REQUEST *request,
                                  ldap_handle_t const *conn, LDAPMessage *entry)
 {
        rlm_rcode_t rcode = RLM_MODULE_OK;
@@ -1230,7 +1268,7 @@ rlm_rcode_t rlm_ldap_check_access(ldap_instance_t const *inst, REQUEST *request,
  * @param inst rlm_ldap configuration.
  * @param request Current request.
  */
-void rlm_ldap_check_reply(ldap_instance_t const *inst, REQUEST *request)
+void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request)
 {
        /*
        *       More warning messages for people who can't be bothered to read the documentation.
@@ -1238,12 +1276,12 @@ void rlm_ldap_check_reply(ldap_instance_t const *inst, REQUEST *request)
        *       Expect_password is set when we process the mapping, and is only true if there was a mapping between
        *       an LDAP attribute and a password reference attribute in the control list.
        */
-       if (inst->expect_password && (debug_flag > 1)) {
-               if (!pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
-                   !pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY) &&
-                   !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)) {
+       if (inst->expect_password && (rad_debug_lvl > 1)) {
+               if (!fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) &&
+                   !fr_pair_find_by_num(request->config, PW_NT_PASSWORD, 0, TAG_ANY) &&
+                   !fr_pair_find_by_num(request->config, PW_USER_PASSWORD, 0, TAG_ANY) &&
+                   !fr_pair_find_by_num(request->config, PW_PASSWORD_WITH_HEADER, 0, TAG_ANY) &&
+                   !fr_pair_find_by_num(request->config, PW_CRYPT_PASSWORD, 0, TAG_ANY)) {
                        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 "
@@ -1275,10 +1313,10 @@ static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t
        conn->rebound = true;   /* not really, but oh well... */
        rad_assert(handle == conn->handle);
 
-       DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->xlat_name, url);
+       DEBUG("rlm_ldap (%s): Rebinding to URL %s", conn->inst->name, url);
 
-       status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password,
-                              conn->inst->admin_sasl_mech, false);
+       status = rlm_ldap_bind(conn->inst, NULL, &conn, conn->inst->admin_identity, conn->inst->admin_password,
+                              &(conn->inst->admin_sasl), false);
        if (status != LDAP_PROC_SUCCESS) {
                ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno);
 
@@ -1323,7 +1361,7 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
        int ldap_errno, ldap_version;
        struct timeval tv;
 
-       ldap_instance_t *inst = instance;
+       rlm_ldap_t *inst = instance;
        ldap_handle_t *conn;
 
        /*
@@ -1337,7 +1375,7 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
        conn->rebound = false;
        conn->referred = false;
 
-       DEBUG("rlm_ldap (%s): Connecting to %s", inst->xlat_name, inst->server);
+       DEBUG("rlm_ldap (%s): Connecting to %s", inst->name, inst->server);
 #ifdef HAVE_LDAP_INITIALIZE
        ldap_errno = ldap_initialize(&conn->handle, inst->server);
        if (ldap_errno != LDAP_SUCCESS) {
@@ -1351,7 +1389,7 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
                goto error;
        }
 #endif
-       DEBUG3("rlm_ldap (%s): New libldap handle %p", inst->xlat_name, conn->handle);
+       DEBUG3("rlm_ldap (%s): New libldap handle %p", inst->name, conn->handle);
 
        /*
         *      We now have a connection structure, but no actual connection.
@@ -1487,8 +1525,8 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
        }
 #endif /* HAVE_LDAP_START_TLS_S */
 
-       status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_dn, conn->inst->password,
-                              conn->inst->admin_sasl_mech, false);
+       status = rlm_ldap_bind(inst, NULL, &conn, conn->inst->admin_identity, conn->inst->admin_password,
+                              &(conn->inst->admin_sasl), false);
        if (status != LDAP_PROC_SUCCESS) {
                goto error;
        }
@@ -1508,7 +1546,7 @@ error:
  * @param inst rlm_ldap configuration.
  * @param request Current request (may be NULL).
  */
-ldap_handle_t *mod_conn_get(ldap_instance_t const *inst, UNUSED REQUEST *request)
+ldap_handle_t *mod_conn_get(rlm_ldap_t const *inst, UNUSED REQUEST *request)
 {
        return fr_connection_get(inst->pool);
 }
@@ -1521,7 +1559,7 @@ ldap_handle_t *mod_conn_get(ldap_instance_t const *inst, UNUSED REQUEST *request
  * @param inst rlm_ldap configuration.
  * @param conn to release.
  */
-void mod_conn_release(ldap_instance_t const *inst, ldap_handle_t *conn)
+void mod_conn_release(rlm_ldap_t const *inst, ldap_handle_t *conn)
 {
        /*
         *      Could have already been free'd due to a previous error.
@@ -1539,7 +1577,7 @@ void mod_conn_release(ldap_instance_t const *inst, ldap_handle_t *conn)
         *      Instead, we let the next caller do the rebind.
         */
        if (conn->referred) {
-               fr_connection_del(inst->pool, conn);
+               fr_connection_close(inst->pool, conn);
                return;
        }
 
index bee1854..ee17d24 100644 (file)
@@ -4,8 +4,9 @@
  * @brief LDAP authorization and authentication module headers.
  *
  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
  * @copyright 2013 Network RADIUS SARL<info@networkradius.com>
- * @copyright 2013 The FreeRADIUS Server Project.
+ * @copyright 2013-2015 The FreeRADIUS Server Project.
  */
 #ifndef _RLM_LDAP_H
 #define _RLM_LDAP_H
 #include "config.h"
 
 /*
+ *     Ensure the have the ldap_create_sort_keylist()
+ *     function too, else we can't use ldap_create_sort_control()
+ */
+#if !defined(LDAP_CREATE_SORT_KEYLIST) || !defined(LDAP_FREE_SORT_KEYLIST)
+#  undef HAVE_LDAP_CREATE_SORT_CONTROL
+#endif
+
+/*
+ *     Because the LTB people define LDAP_VENDOR_VERSION_PATCH
+ *     as X, which precludes its use in printf statements *sigh*
+ *
+ *     Identifiers that are not macros, all evaluate to 0,
+ *     which is why this works.
+ */
+#if !defined(LDAP_VENDOR_VERSION_PATCH) || LDAP_VENDOR_VERSION_PATCH == 0
+#  undef LDAP_VENDOR_VERSION_PATCH
+#  define LDAP_VENDOR_VERSION_PATCH 0
+#endif
+
+/*
  *      For compatibility with other LDAP libraries
  */
 #if !defined(LDAP_SCOPE_BASE) && defined(LDAP_SCOPE_BASEOBJECT)
 #  define LDAP_CONST
 #endif
 
+#if defined(HAVE_LDAP_URL_PARSE) && defined(HAVE_LDAP_IS_LDAP_URL) && defined(HAVE_LDAP_URL_DESC2STR)
+#  define LDAP_CAN_PARSE_URLS
+#endif
+
+#define MOD_PREFIX                     "rlm_ldap"      //!< The name of the module.
+
 #define LDAP_MAX_ATTRMAP               128             //!< Maximum number of mappings between LDAP and
                                                        //!< FreeRADIUS attributes.
 #define LDAP_MAP_RESERVED              4               //!< Number of additional items to allocate in expanded
 #define LDAP_MAX_GROUP_NAME_LEN                128             //!< Maximum name of a group name.
 #define LDAP_MAX_ATTR_STR_LEN          256             //!< Maximum length of an xlat expanded LDAP attribute.
 #define LDAP_MAX_FILTER_STR_LEN                1024            //!< Maximum length of an xlat expanded filter.
-#define LDAP_MAX_DN_STR_LEN            2048            //!< Maximum length of an xlat expanded DN.
+#define LDAP_MAX_DN_STR_LEN            1024            //!< Maximum length of an xlat expanded DN.
+
+#define LDAP_VIRTUAL_DN_ATTR           "dn"            //!< 'Virtual' attribute which maps to the DN of the object.
 
 typedef struct ldap_acct_section {
        CONF_SECTION    *cs;                            //!< Section configuration.
 
-       char const *reference;                          //!< Configuration reference string.
+       char const      *reference;                     //!< Configuration reference string.
 } ldap_acct_section_t;
 
+typedef struct ldap_sasl {
+       char const      *mech;                          //!< SASL mech(s) to try.
+       char const      *proxy;                         //!< Identity to proxy.
+       char const      *realm;                         //!< Kerberos realm.
+} ldap_sasl;
+
+typedef struct ldap_sasl_dynamic {
+       vp_tmpl_t       *mech;                          //!< SASL mech(s) to try.
+       vp_tmpl_t       *proxy;                         //!< Identity to proxy.
+       vp_tmpl_t       *realm;                         //!< Kerberos realm.
+} ldap_sasl_dynamic;
+
 typedef struct ldap_instance {
        CONF_SECTION    *cs;                            //!< Main configuration section for this instance.
        fr_connection_pool_t *pool;                     //!< Connection pool instance.
 
-       char const      *config_server;                 //!< server from the config files
+       char const      *config_server;                 //!< Server set in the config.
        char            *server;                        //!< Initial server to bind to.
        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
+       char const      *admin_identity;                //!< Identity we bind as when we need to query the LDAP
                                                        //!< directory.
-       char const      *password;                      //!< Password used in administrative bind.
+       char const      *admin_password;                //!< Password used in administrative bind.
 
-       char const      *admin_sasl_mech;               //!< SASL mechanism to use for administrative binds.
+       ldap_sasl       admin_sasl;                     //!< SASL parameters used when binding as the admin.
 
        char const      *dereference_str;               //!< When to dereference (never, searching, finding, always)
        int             dereference;                    //!< libldap value specifying dereferencing behaviour.
@@ -98,7 +139,7 @@ typedef struct ldap_instance {
 
        uint32_t        ldap_debug;                     //!< Debug flag for the SDK.
 
-       char const      *xlat_name;                     //!< Instance name.
+       char const      *name;                          //!< Instance name.
 
        bool            expect_password;                //!< True if the user_map included a mapping between an LDAP
                                                        //!< attribute and one of our password reference attributes.
@@ -106,15 +147,17 @@ typedef struct ldap_instance {
        /*
         *      RADIUS attribute to LDAP attribute maps
         */
-       value_pair_map_t *user_map;                     //!< Attribute map applied to users and profiles.
+       vp_map_t        *user_map;                      //!< Attribute map applied to users and profiles.
 
        /*
         *      User object attributes and filters
         */
-       char const      *user_sasl_mech;                //!< SASL mechanism to use for user binds.
-       value_pair_tmpl_t *userobj_filter;              //!< Filter to retrieve only user objects.
-       value_pair_tmpl_t *userobj_base_dn;             //!< DN to search for users under.
+       vp_tmpl_t       *userobj_filter;                //!< Filter to retrieve only user objects.
+       vp_tmpl_t       *userobj_base_dn;               //!< DN to search for users under.
        char const      *userobj_scope_str;             //!< Scope (sub, one, base).
+       char const      *userobj_sort_by;               //!< List of attributes to sort by.
+       LDAPControl     *userobj_sort_ctrl;             //!< Server side sort control.
+
        int             userobj_scope;                  //!< Search scope.
 
        char const      *userobj_membership_attr;       //!< Attribute that describes groups the user is a member of.
@@ -125,12 +168,13 @@ typedef struct ldap_instance {
        char const      *valuepair_attr;                //!< Generic dynamic mapping attribute, contains a RADIUS
                                                        //!< attribute and value.
 
+       ldap_sasl_dynamic user_sasl;                    //!< SASL parameters used when binding as the user.
+
        /*
         *      Group object attributes and filters
         */
-
        char const      *groupobj_filter;               //!< Filter to retrieve only group objects.
-       value_pair_tmpl_t *groupobj_base_dn;            //!< DN to search for users under.
+       vp_tmpl_t       *groupobj_base_dn;              //!< DN to search for users under.
        char const      *groupobj_scope_str;            //!< Scope (sub, one, base).
        int             groupobj_scope;                 //!< Search scope.
 
@@ -170,13 +214,13 @@ typedef struct ldap_instance {
        /*
         *      Profiles
         */
-       value_pair_tmpl_t *default_profile;             //!< If this is set, we will search for a profile object
+       vp_tmpl_t       *default_profile;               //!< If this is set, we will search for a profile object
                                                        //!< with this name, and map any attributes it contains.
                                                        //!< No value should be set if profiles are not being used
                                                        //!< as there is an associated performance penalty.
        char const      *profile_attr;                  //!< Attribute that identifies profiles to apply. May appear
                                                        //!< in userobj or groupobj.
-       value_pair_tmpl_t *profile_filter;              //!< Filter to retrieve only retrieve group objects.
+       vp_tmpl_t       *profile_filter;                //!< Filter to retrieve only retrieve group objects.
 
        /*
         *      Accounting
@@ -246,31 +290,46 @@ typedef struct ldap_instance {
 #endif
 
        LDAP            *handle;                        //!< Hack for OpenLDAP libldap global initialisation.
-} ldap_instance_t;
+} rlm_ldap_t;
 
+/** Tracks the state of a libldap connection handle
+ *
+ */
 typedef struct ldap_handle {
-       LDAP            *handle;                        //!< LDAP LD handle.
-       int             rebound;                        //!< Whether the connection has been rebound to something
+       LDAP            *handle;                        //!< libldap handle.
+       bool            rebound;                        //!< Whether the connection has been rebound to something
                                                        //!< other than the admin user.
-       int             referred;                       //!< Whether the connection is now established a server
+       bool            referred;                       //!< Whether the connection is now established a server
                                                        //!< other than the configured one.
-       ldap_instance_t *inst;                          //!< rlm_ldap configuration.
+       rlm_ldap_t      *inst;                          //!< rlm_ldap configuration.
 } ldap_handle_t;
 
-typedef struct rlm_ldap_map_xlat {
-       value_pair_map_t const *maps;
-       char const *attrs[LDAP_MAX_ATTRMAP + LDAP_MAP_RESERVED + 1]; //!< Reserve some space for access attributes
-                                                                    //!< and NULL termination.
-       int count;
-} rlm_ldap_map_xlat_t;
-
+/** Result of expanding the RHS of a set of maps
+ *
+ * Used to store the array of attributes we'll be querying for.
+ */
+typedef struct rlm_ldap_map_exp {
+       vp_map_t const *maps;                           //!< Head of list of maps we expanded the RHS of.
+       char const      *attrs[LDAP_MAX_ATTRMAP + LDAP_MAP_RESERVED + 1]; //!< Reserve some space for access attributes
+                                                       //!< and NULL termination.
+       TALLOC_CTX      *ctx;                           //!< Context to allocate new attributes in.
+       int             count;                          //!< Index on next free element.
+} rlm_ldap_map_exp_t;
+
+/** Contains a collection of values
+ *
+ */
 typedef struct rlm_ldap_result {
        struct berval   **values;                       //!< libldap struct containing bv_val (char *)
                                                        //!< and length bv_len.
        int             count;                          //!< Number of values.
 } rlm_ldap_result_t;
 
+/** Codes returned by rlm_ldap internal functions
+ *
+ */
 typedef enum {
+       LDAP_PROC_CONTINUE = 1,                         //!< Operation is in progress.
        LDAP_PROC_SUCCESS = 0,                          //!< Operation was successfull.
 
        LDAP_PROC_ERROR = -1,                           //!< Unrecoverable library/server error.
@@ -294,22 +353,22 @@ typedef enum {
  *     simplifies switching certain messages from the request log to
  *     the main log.
  */
-#define LDAP_INFO(fmt, ...) INFO("rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
-#define LDAP_WARN(fmt, ...) WARN("rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
+#define LDAP_INFO(fmt, ...) INFO("rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__)
+#define LDAP_WARN(fmt, ...) WARN("rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__)
 
-#define LDAP_DBGW(fmt, ...) radlog(L_DBG_WARN, "rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
+#define LDAP_DBGW(fmt, ...) radlog(L_DBG_WARN, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__)
 #define LDAP_DBGW_REQ(fmt, ...) do { if (request) {RWDEBUG(fmt, ##__VA_ARGS__);} else {LDAP_DBGW(fmt, ##__VA_ARGS__);}} while (0)
 
-#define LDAP_DBG(fmt, ...) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
+#define LDAP_DBG(fmt, ...) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__)
 #define LDAP_DBG_REQ(fmt, ...) do { if (request) {RDEBUG(fmt, ##__VA_ARGS__);} else {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0)
 
-#define LDAP_DBG2(fmt, ...) if (debug_flag >= L_DBG_LVL_2) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
-#define LDAP_DBG_REQ2(fmt, ...) do { if (request) {RDEBUG2(fmt, ##__VA_ARGS__);} else if (debug_flag >= L_DBG_LVL_2) {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0)
+#define LDAP_DBG2(fmt, ...) if (rad_debug_lvl >= L_DBG_LVL_2) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__)
+#define LDAP_DBG_REQ2(fmt, ...) do { if (request) {RDEBUG2(fmt, ##__VA_ARGS__);} else if (rad_debug_lvl >= L_DBG_LVL_2) {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0)
 
-#define LDAP_DBG3(fmt, ...) if (debug_flag >= L_DBG_LVL_3) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
-#define LDAP_DBG_REQ3(fmt, ...) do { if (request) {RDEBUG3(fmt, ##__VA_ARGS__);} else if (debug_flag >= L_DBG_LVL_3) {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0)
+#define LDAP_DBG3(fmt, ...) if (rad_debug_lvl >= L_DBG_LVL_3) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__)
+#define LDAP_DBG_REQ3(fmt, ...) do { if (request) {RDEBUG3(fmt, ##__VA_ARGS__);} else if (rad_debug_lvl >= L_DBG_LVL_3) {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0)
 
-#define LDAP_ERR(fmt, ...) ERROR("rlm_ldap (%s): " fmt, inst->xlat_name, ##__VA_ARGS__)
+#define LDAP_ERR(fmt, ...) ERROR("rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__)
 #define LDAP_ERR_REQ(fmt, ...) do { if (request) {REDEBUG(fmt, ##__VA_ARGS__);} else {LDAP_ERR(fmt, ##__VA_ARGS__);}} while (0)
 
 #define LDAP_EXT() if (extra) LDAP_ERR(extra)
@@ -329,72 +388,73 @@ size_t rlm_ldap_normalise_dn(char *out, char const *in);
 
 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, char const *sasl_mech, bool retry);
+ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
+                          char const *password, ldap_sasl *sasl, bool retry);
 
 char const *rlm_ldap_error_str(ldap_handle_t const *conn);
 
-ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+ldap_rcode_t rlm_ldap_search(LDAPMessage **result, rlm_ldap_t const *inst, REQUEST *request,
+                            ldap_handle_t **pconn,
                             char const *dn, int scope, char const *filter, char const * const *attrs,
-                            LDAPMessage **result);
+                            LDAPControl **serverctrls, LDAPControl **clientctrls);
 
-ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+ldap_rcode_t rlm_ldap_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                             char const *dn, LDAPMod *mods[]);
 
-char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+char const *rlm_ldap_find_user(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                               char const *attrs[], bool force, LDAPMessage **result, rlm_rcode_t *rcode);
 
-rlm_rcode_t rlm_ldap_check_access(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t const *conn,
+rlm_rcode_t rlm_ldap_check_access(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn,
                                  LDAPMessage *entry);
 
-void rlm_ldap_check_reply(ldap_instance_t const *inst, REQUEST *request);
+void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request);
 
 /*
  *     ldap.c - Callbacks for the connection pool API.
  */
+ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn,
+                            LDAPMessage **result, char const **error, char **extra);
+
 char *rlm_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in);
 
 void *mod_conn_create(TALLOC_CTX *ctx, void *instance);
 
-ldap_handle_t *mod_conn_get(ldap_instance_t const *inst, REQUEST *request);
+ldap_handle_t *mod_conn_get(rlm_ldap_t const *inst, REQUEST *request);
 
-void mod_conn_release(ldap_instance_t const *inst, ldap_handle_t *conn);
+void mod_conn_release(rlm_ldap_t const *inst, ldap_handle_t *conn);
 
 /*
  *     groups.c - Group membership functions.
  */
-rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                                       LDAPMessage *entry, char const *attr);
 
-rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn);
+rlm_rcode_t rlm_ldap_cacheable_groupobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn);
 
-rlm_rcode_t rlm_ldap_check_groupobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+rlm_rcode_t rlm_ldap_check_groupobj_dynamic(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                                            VALUE_PAIR *check);
 
-rlm_rcode_t rlm_ldap_check_userobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+rlm_rcode_t rlm_ldap_check_userobj_dynamic(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
                                           char const *dn, VALUE_PAIR *check);
 
-rlm_rcode_t rlm_ldap_check_cached(ldap_instance_t const *inst, REQUEST *request, VALUE_PAIR *check);
+rlm_rcode_t rlm_ldap_check_cached(rlm_ldap_t const *inst, REQUEST *request, VALUE_PAIR *check);
 
 /*
  *     attrmap.c - Attribute mapping code.
  */
-int rlm_ldap_map_verify(value_pair_map_t *map, void *instance);
+int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx);
 
-void rlm_ldap_map_xlat_free(rlm_ldap_map_xlat_t const *expanded);
+int rlm_ldap_map_verify(vp_map_t *map, void *instance);
 
-int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded);
+int rlm_ldap_map_expand(rlm_ldap_map_exp_t *expanded, REQUEST *request, vp_map_t const *maps);
 
-int 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);
+int rlm_ldap_map_do(rlm_ldap_t const *inst, REQUEST *request, LDAP *handle,
+                   rlm_ldap_map_exp_t const *expanded, LDAPMessage *entry);
 
 /*
  *     clients.c - Dynamic clients (bulk load).
  */
-int  rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs);
+int  rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTION *cs);
 
 /*
  *     edir.c - Magic extensions for Novell
@@ -403,4 +463,11 @@ int nmasldap_get_password(LDAP *ld, char const *dn, char *password, size_t *len)
 
 char const *edir_errstr(int code);
 
+/*
+ *     sasl.s - SASL bind functions
+ */
+ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request,
+                                      ldap_handle_t *pconn, char const *dn,
+                                      char const *password, ldap_sasl *sasl,
+                                      char const **error, char **error_extra);
 #endif
index 20b0699..d70d005 100644 (file)
@@ -22,8 +22,8 @@
  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
  * @author Alan DeKok <aland@freeradius.org>
  *
- * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
- * @copyright 2012-2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2012,2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013,2015 Network RADIUS SARL <info@networkradius.com>
  * @copyright 2012 Alan DeKok <aland@freeradius.org>
  * @copyright 1999-2013 The FreeRADIUS Server Project.
  */
@@ -70,6 +70,20 @@ static FR_NAME_NUMBER const ldap_dereference[] = {
        {  NULL , -1 }
 };
 
+static CONF_PARSER sasl_mech_dynamic[] = {
+       { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL | PW_TYPE_NOT_EMPTY, ldap_sasl_dynamic, mech), NULL },
+       { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, proxy), NULL },
+       { "realm", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_sasl_dynamic, realm), NULL },
+       CONF_PARSER_TERMINATOR
+};
+
+static CONF_PARSER sasl_mech_static[] = {
+       { "mech", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_NOT_EMPTY, ldap_sasl, mech), NULL },
+       { "proxy", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, proxy), NULL },
+       { "realm", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_sasl, realm), NULL },
+       CONF_PARSER_TERMINATOR
+};
+
 /*
  *     TLS Configuration
  */
@@ -77,79 +91,76 @@ static CONF_PARSER tls_config[] = {
        /*
         *      Deprecated attributes
         */
-       { "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 },
+       { "cacertfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_ca_file), NULL },
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_ca_file), 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 },
+       { "cacertdir", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_ca_path), NULL },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_ca_path), 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 },
+       { "certfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_certificate_file), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_certificate_file), NULL },
 
-       { "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
+       { "keyfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_private_key_file), NULL }, // OK if it changes on HUP
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_private_key_file), NULL }, // OK if it changes on HUP
 
-       { "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 },
+       { "randfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_ldap_t, tls_random_file), NULL },
+       { "random_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_ldap_t, tls_random_file), NULL },
 
        /*
         *      LDAP Specific TLS attributes
         */
-       { "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 }
+       { "start_tls", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, start_tls), "no" },
+       { "require_cert", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, tls_require_cert_str), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 
 static CONF_PARSER profile_config[] = {
-       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_instance_t, profile_filter), "(&)" },    //!< Correct filter for when the DN is known.
-       { "attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, profile_attr), NULL },
-       { "default", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_instance_t, default_profile), NULL },
-
-       { NULL, -1, 0, NULL, NULL }
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, profile_filter), "(&)" }, //!< Correct filter for when the DN is known.
+       { "attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, profile_attr), NULL },
+       { "default", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, default_profile), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
  *     User configuration
  */
 static CONF_PARSER user_config[] = {
-       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_instance_t, userobj_filter), NULL },
-       { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_scope_str), "sub" },
-       { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, ldap_instance_t, userobj_base_dn), "" },
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, userobj_filter), NULL },
+       { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_scope_str), "sub" },
+       { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, userobj_base_dn), "" },
+       { "sort_by", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_sort_by), NULL },
 
-       { "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" },
+       { "access_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_access_attr), NULL },
+       { "access_positive", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, access_positive), "yes" },
 
-       { "sasl_mech", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, user_sasl_mech), NULL },
-
-       { NULL, -1, 0, NULL, NULL }
+       /* Should be deprecated */
+       { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, user_sasl), (void const *) sasl_mech_dynamic },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
  *     Group configuration
  */
 static CONF_PARSER group_config[] = {
-       { "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 | PW_TYPE_TMPL, ldap_instance_t, groupobj_base_dn), "" },
-
-       { "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 }
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_filter), NULL },
+       { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_scope_str), "sub" },
+       { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_ldap_t, groupobj_base_dn), "" },
+
+       { "name_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, groupobj_name_attr), "cn" },
+       { "membership_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, userobj_membership_attr), NULL },
+       { "membership_filter", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_ldap_t, groupobj_membership_filter), NULL },
+       { "cacheable_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, cacheable_group_name), "no" },
+       { "cacheable_dn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, cacheable_group_dn), "no" },
+       { "cache_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, cache_attribute), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 static CONF_PARSER client_config[] = {
-       { "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), "" },
-
-       { NULL, -1, 0, NULL, NULL }
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_filter), NULL },
+       { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_scope_str), "sub" },
+       { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, clientobj_base_dn), "" },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -157,8 +168,7 @@ static CONF_PARSER client_config[] = {
  */
 static const CONF_PARSER acct_section_config[] = {
        { "reference", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, ldap_acct_section_t, reference), "." },
-
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -170,61 +180,61 @@ static CONF_PARSER option_config[] = {
        /*
         *      Debugging flags to the server
         */
-       { "ldap_debug", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, ldap_debug), "0x0000" },
+       { "ldap_debug", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, ldap_debug), "0x0000" },
 
-       { "dereference", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, dereference_str), NULL },
+       { "dereference", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, dereference_str), NULL },
 
-       { "chase_referrals", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, chase_referrals), NULL },
+       { "chase_referrals", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, chase_referrals), NULL },
 
-       { "rebind", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, rebind), NULL },
+       { "rebind", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, rebind), NULL },
 
 #ifdef LDAP_OPT_NETWORK_TIMEOUT
        /* timeout on network activity */
-       { "net_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, net_timeout), "10" },
+       { "net_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, net_timeout), "10" },
 #endif
 
        /* timeout for search results */
-       { "res_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, res_timeout), "20" },
+       { "res_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, res_timeout), "20" },
 
        /* allow server unlimited time for search (server-side limit) */
-       { "srv_timelimit", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, srv_timelimit), "20" },
+       { "srv_timelimit", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, srv_timelimit), "20" },
 
 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
-       { "idle", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_idle), "60" },
+       { "idle", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_idle), "60" },
 #endif
 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
-       { "probes", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_probes), "3" },
+       { "probes", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_probes), "3" },
 #endif
 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
-       { "interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_interval), "30" },
+       { "interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ldap_t, keepalive_interval), "30" },
 #endif
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 
 static const CONF_PARSER module_config[] = {
-       { "server", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, config_server), NULL },     /* Do not set to required */
-       { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, ldap_instance_t, port), NULL },
+       { "server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, config_server), NULL },  /* Do not set to required */
+       { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, rlm_ldap_t, port), NULL },
 
-       { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, ldap_instance_t, password), NULL },
-       { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, admin_dn), NULL },
-       { "sasl_mech", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, admin_sasl_mech), NULL },
+       { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, admin_identity), NULL },
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_ldap_t, admin_password), NULL },
 
-       { "valuepair_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, valuepair_attr), NULL },
+       { "sasl", FR_CONF_OFFSET(PW_TYPE_SUBSECTION, rlm_ldap_t, admin_sasl), (void const *) sasl_mech_static },
+
+       { "valuepair_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_ldap_t, valuepair_attr), NULL },
 
 #ifdef WITH_EDIR
        /* support for eDirectory Universal Password */
-       { "edir", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, edir), NULL }, /* NULL defaults to "no" */
+       { "edir", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, edir), NULL }, /* NULL defaults to "no" */
 
        /*
         *      Attempt to bind with the cleartext password we got from eDirectory
         *      Universal password for additional authorization checks.
         */
-       { "edir_autz", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, edir_autz), NULL }, /* NULL defaults to "no" */
+       { "edir_autz", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, edir_autz), NULL }, /* NULL defaults to "no" */
 #endif
 
-       { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, do_clients), NULL }, /* NULL defaults to "no" */
+       { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ldap_t, do_clients), NULL }, /* NULL defaults to "no" */
 
        { "user", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) user_config },
 
@@ -237,8 +247,7 @@ static const CONF_PARSER module_config[] = {
        { "options", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) option_config },
 
        { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
-
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 static ssize_t ldapquote_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
@@ -251,17 +260,21 @@ static ssize_t ldapquote_xlat(UNUSED void *instance, REQUEST *request, char cons
  */
 static ssize_t ldap_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
 {
-       ldap_rcode_t status;
-       size_t len = 0;
-       ldap_instance_t *inst = instance;
-       LDAPURLDesc *ldap_url;
-       LDAPMessage *result = NULL;
-       LDAPMessage *entry = NULL;
-       struct berval **values;
-       ldap_handle_t *conn;
-       int ldap_errno;
-       char const *url;
-       char const **attrs;
+       ldap_rcode_t            status;
+       size_t                  len = 0;
+       rlm_ldap_t              *inst = instance;
+
+       LDAPURLDesc             *ldap_url;
+       LDAPMessage             *result = NULL;
+       LDAPMessage             *entry = NULL;
+
+       struct berval           **values;
+
+       ldap_handle_t           *conn;
+       int                     ldap_errno;
+
+       char const              *url;
+       char const              **attrs;
 
        url = fmt;
 
@@ -292,8 +305,8 @@ static ssize_t ldap_xlat(void *instance, REQUEST *request, char const *fmt, char
 
        memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs));
 
-       status = rlm_ldap_search(inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter,
-                                attrs, &result);
+       status = rlm_ldap_search(&result, inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope,
+                                ldap_url->lud_filter, attrs, NULL, NULL);
        switch (status) {
        case LDAP_PROC_SUCCESS:
                break;
@@ -346,12 +359,14 @@ free_urldesc:
  * @param check Which group to check for user membership.
  * @param check_pairs Unknown.
  * @param reply_pairs Unknown.
- * @return 1 on failure (or if the user is not a member), else 0.
+ * @return
+ *     - 1 on failure (or if the user is not a member).
+ *     - 0 on success.
  */
 static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
                             UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
 {
-       ldap_instance_t *inst = instance;
+       rlm_ldap_t      *inst = instance;
        rlm_rcode_t     rcode;
 
        bool            found = false;
@@ -378,7 +393,7 @@ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR
 
                MEM(norm = talloc_memdup(check, check->vp_strvalue, talloc_array_length(check->vp_strvalue)));
                rlm_ldap_normalise_dn(norm, check->vp_strvalue);
-               pairstrsteal(check, norm);
+               fr_pair_value_strsteal(check, norm);
        }
        if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
                switch (rlm_ldap_check_cached(inst, request, check)) {
@@ -466,9 +481,9 @@ finish:
  */
 static int mod_detach(void *instance)
 {
-       ldap_instance_t *inst = instance;
+       rlm_ldap_t *inst = instance;
 
-       fr_connection_pool_delete(inst->pool);
+       fr_connection_pool_free(inst->pool);
 
        if (inst->user_map) {
                talloc_free(inst->user_map);
@@ -487,6 +502,10 @@ static int mod_detach(void *instance)
 #endif
        }
 
+#ifdef HAVE_LDAP_CREATE_SORT_CONTROL
+       if (inst->userobj_sort_ctrl) ldap_control_free(inst->userobj_sort_ctrl);
+#endif
+
        return 0;
 }
 
@@ -498,9 +517,11 @@ static int mod_detach(void *instance)
  * @param[in] parent of the config section.
  * @param[out] config to write the sub section parameters to.
  * @param[in] comp The section name were parsing the config for.
- * @return 0 on success, else < 0 on failure.
+ * @return
+ *     - 0 on success.
+ *     - < 0 on failure.
  */
-static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
+static int parse_sub_section(rlm_ldap_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
                             rlm_components_t comp)
 {
        CONF_SECTION *cs;
@@ -510,7 +531,7 @@ static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_a
        cs = cf_section_sub_find(parent, name);
        if (!cs) {
                DEBUG2("rlm_ldap (%s): Couldn't find configuration for %s, will return NOOP for calls "
-                      "from this section", inst->xlat_name, name);
+                      "from this section", inst->name, name);
 
                return 0;
        }
@@ -527,13 +548,87 @@ static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_a
        return 0;
 }
 
+/** Bootstrap the module
+ *
+ * Define attributes.
+ *
+ * @param conf to parse.
+ * @param instance configuration data.
+ * @return
+ *     - 0 on success.
+ *     - < 0 on failure.
+ */
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
+{
+       rlm_ldap_t *inst = instance;
+
+       inst->name = cf_section_name2(conf);
+       if (!inst->name) {
+               inst->name = cf_section_name1(conf);
+       }
+
+       /*
+        *      Group comparison checks.
+        */
+       if (cf_section_name2(conf)) {
+               char buffer[256];
+
+               snprintf(buffer, sizeof(buffer), "%s-LDAP-Group", inst->name);
+
+               if (paircompare_register_byname(buffer, dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst) < 0) {
+                       LDAP_ERR("Error registering group comparison: %s", fr_strerror());
+                       goto error;
+               }
+
+               inst->group_da = dict_attrbyname(buffer);
+
+               /*
+                *      We're the default instance
+                */
+       } else {
+               if (paircompare_register_byname("LDAP-Group", dict_attrbyvalue(PW_USER_NAME, 0),
+                                               false, rlm_ldap_groupcmp, inst) < 0) {
+                       LDAP_ERR("Error registering group comparison: %s", fr_strerror());
+                       goto error;
+               }
+
+               inst->group_da = dict_attrbyname("LDAP-Group");
+       }
+
+       /*
+        *      Setup the cache attribute
+        */
+       if (inst->cache_attribute) {
+               ATTR_FLAGS flags;
+
+               memset(&flags, 0, sizeof(flags));
+               if (dict_addattr(inst->cache_attribute, -1, 0, PW_TYPE_STRING, flags) < 0) {
+                       LDAP_ERR("Error creating cache attribute: %s", fr_strerror());
+               error:
+                       return -1;
+
+               }
+               inst->cache_da = dict_attrbyname(inst->cache_attribute);
+       } else {
+               inst->cache_da = inst->group_da;        /* Default to the group_da */
+       }
+
+       xlat_register(inst->name, ldap_xlat, rlm_ldap_escape_func, inst);
+       xlat_register("ldapquote", ldapquote_xlat, NULL, inst);
+
+       return 0;
+}
+
+
 /** Instantiate the module
  *
  * Creates a new instance of the module reading parameters from a configuration section.
  *
  * @param conf to parse.
- * @param instance Where to write pointer to configuration data.
- * @return 0 on success < 0 on failure.
+ * @param instance configuration data.
+ * @return
+ *     - 0 on success.
+ *     - < 0 on failure.
  */
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
@@ -543,7 +638,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        CONF_ITEM       *ci;
 
        CONF_SECTION *options, *update;
-       ldap_instance_t *inst = instance;
+       rlm_ldap_t *inst = instance;
 
        inst->cs = conf;
 
@@ -552,11 +647,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                inst->chase_referrals_unset = true;      /* use OpenLDAP defaults */
        }
 
-       inst->xlat_name = cf_section_name2(conf);
-       if (!inst->xlat_name) {
-               inst->xlat_name = cf_section_name1(conf);
-       }
-
        /*
         *      Only needs to be done once, prevents races in environment
         *      initialisation within libldap.
@@ -580,13 +670,20 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
                ldap_errno = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info);
                if (ldap_errno == LDAP_OPT_SUCCESS) {
-                       if (strcmp(info.ldapai_vendor_name, LDAP_VENDOR_NAME) != 0) {
+                       /*
+                        *      Don't generate warnings if the compile type vendor name
+                        *      is found within the link time vendor name.
+                        *
+                        *      This allows the server to be built against OpenLDAP but
+                        *      run with Symas OpenLDAP.
+                        */
+                       if (strcasestr(info.ldapai_vendor_name, LDAP_VENDOR_NAME) == NULL) {
                                WARN("rlm_ldap: libldap vendor changed since the server was built");
                                WARN("rlm_ldap: linked: %s, built: %s", info.ldapai_vendor_name, LDAP_VENDOR_NAME);
                        }
 
-                       if (info.ldapai_vendor_version != LDAP_VENDOR_VERSION) {
-                               WARN("rlm_ldap: libldap version changed since the server was built");
+                       if (info.ldapai_vendor_version < LDAP_VENDOR_VERSION) {
+                               WARN("rlm_ldap: libldap older than the version the server was built against");
                                WARN("rlm_ldap: linked: %i, built: %i",
                                     info.ldapai_vendor_version, LDAP_VENDOR_VERSION);
                        }
@@ -607,8 +704,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        /*
         *      If the configuration parameters can't be parsed, then fail.
         */
-       if ((parse_sub_section(inst, conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
-           (parse_sub_section(inst, conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
+       if ((parse_sub_section(inst, conf, &inst->accounting, MOD_ACCOUNTING) < 0) ||
+           (parse_sub_section(inst, conf, &inst->postauth, MOD_POST_AUTH) < 0)) {
                cf_log_err_cs(conf, "Failed parsing configuration");
 
                goto error;
@@ -638,16 +735,24 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                }
        }
 
-#ifndef HAVE_LDAP_SASL_BIND
-       if (inst->user_sasl_mech) {
-               cf_log_err_cs(conf, "Configuration item 'user.sasl_mech' not supported.  "
+#ifndef WITH_SASL
+       if (inst->user_sasl.mech) {
+               cf_log_err_cs(conf, "Configuration item 'user.sasl.mech' not supported.  "
                              "Linked libldap does not provide ldap_sasl_bind function");
                goto error;
        }
 
-       if (inst->admin_sasl_mech) {
-               cf_log_err_cs(conf, "Configuration item 'sasl_mech' not supported.  "
-                             "Linked libldap does not provide ldap_sasl_bind function");
+       if (inst->admin_sasl.mech) {
+               cf_log_err_cs(conf, "Configuration item 'sasl.mech' not supported.  "
+                             "Linked libldap does not provide ldap_sasl_interactive_bind function");
+               goto error;
+       }
+#endif
+
+#ifndef HAVE_LDAP_CREATE_SORT_CONTROL
+       if (inst->userobj_sort_by) {
+               cf_log_err_cs(conf, "Configuration item 'sort_by' not supported.  "
+                             "Linked libldap does not provide ldap_create_sort_control function");
                goto error;
        }
 #endif
@@ -668,7 +773,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                bool            first = true;
 
                cp = cf_pair_find(conf, "server");
-               rad_assert(cp != NULL);
+               if (!cp) {
+                       cf_log_err_cs(conf, "Configuration item 'server' must have a value");
+                       return -1;
+               }
 
                value = cf_pair_value(cp);
 
@@ -738,6 +846,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
                value = cf_pair_value(cp);
 
+#ifdef LDAP_CAN_PARSE_URLS
                /*
                 *      Split original server value out into URI, server and port
                 *      so whatever initialization function we use later will have
@@ -745,65 +854,119 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                 */
                if (ldap_is_ldap_url(value)) {
                        LDAPURLDesc     *ldap_url;
-                       int             port = -1;
+                       bool            set_port_maybe = true;
+                       int             default_port = LDAP_PORT;
+                       char            *p;
 
                        if (ldap_url_parse(value, &ldap_url)){
                                cf_log_err_cs(conf, "Parsing LDAP URL \"%s\" failed", value);
+                       ldap_url_error:
+                               ldap_free_urldesc(ldap_url);
                                return -1;
                        }
 
-#ifndef HAVE_LDAP_INITIALIZE
+                       if (ldap_url->lud_dn) {
+                               cf_log_err_cs(conf, "Base DN cannot be specified via server URL");
+                               goto ldap_url_error;
+                       }
+
+                       if (ldap_url->lud_attrs && ldap_url->lud_attrs[0]) {
+                               cf_log_err_cs(conf, "Attribute list cannot be specified via server URL");
+                               goto ldap_url_error;
+                       }
+
+                       /*
+                        *      ldap_url_parse sets this to base by default.
+                        */
+                       if (ldap_url->lud_scope != LDAP_SCOPE_BASE) {
+                               cf_log_err_cs(conf, "Scope cannot be specified via server URL");
+                               goto ldap_url_error;
+                       }
+                       ldap_url->lud_scope = -1;       /* Otherwise LDAP adds ?base */
+
+                       /*
+                        *      The public ldap_url_parse function sets the default
+                        *      port, so we have to discover whether a port was
+                        *      included ourselves.
+                        */
+                       if ((p = strchr(value, ']')) && (p[1] == ':')) {                        /* IPv6 */
+                               set_port_maybe = false;
+                       } else if ((p = strchr(value, ':')) && (p = strchr(p + 1, ':'))) {      /* IPv4 */
+                               set_port_maybe = false;
+                       }
+
+                       /* We allow extensions */
+
+#  ifdef HAVE_LDAP_INITIALIZE
+                       {
+                               char *url;
+
+                               /*
+                                *      Figure out the default port from the URL
+                                */
+                               if (ldap_url->lud_scheme) {
+                                       if (strcmp(ldap_url->lud_scheme, "ldaps") == 0) {
+                                               if (inst->start_tls == true) {
+                                                       cf_log_err_cs(conf, "ldaps:// scheme is not compatible "
+                                                                     "with 'start_tls'");
+                                                       goto ldap_url_error;
+                                               }
+                                               default_port = LDAPS_PORT;
+
+                                       } else if (strcmp(ldap_url->lud_scheme, "ldapi") == 0) {
+                                               set_port_maybe = false; /* Unix socket, no port */
+                                       }
+                               }
+
+                               if (set_port_maybe) {
+                                       /*
+                                        *      URL port overrides configured port.
+                                        */
+                                       ldap_url->lud_port = inst->port;
+
+                                       /*
+                                        *      If there's no URL port, then set it to the default
+                                        *      this is so debugging messages show explicitly
+                                        *      the port we're connecting to.
+                                        */
+                                       if (!ldap_url->lud_port) ldap_url->lud_port = default_port;
+                               }
+
+                               url = ldap_url_desc2str(ldap_url);
+                               if (!url) {
+                                       cf_log_err_cs(conf, "Failed recombining URL components");
+                                       goto ldap_url_error;
+                               }
+                               inst->server = talloc_asprintf_append(inst->server, "%s ", url);
+                               free(url);
+                       }
+#  else
                        /*
                         *      No LDAP initialize function.  Can't specify a scheme.
                         */
                        if (ldap_url->lud_scheme &&
-                           (strcmp(ldap_url->lud_scheme, "ldaps") == 0) ||
+                           ((strcmp(ldap_url->lud_scheme, "ldaps") == 0) ||
                            (strcmp(ldap_url->lud_scheme, "ldapi") == 0) ||
-                           (strcmp(ldap_url->lud_scheme, "cldap") == 0)) {
+                           (strcmp(ldap_url->lud_scheme, "cldap") == 0))) {
                                cf_log_err_cs(conf, "%s is not supported by linked libldap",
                                              ldap_url->lud_scheme);
                                return -1;
                        }
 
-#else
                        /*
-                        *      Figure out the port from the URL
+                        *      URL port over-rides the configured
+                        *      port.  But if there's no configured
+                        *      port, we use the hard-coded default.
                         */
-                       if (ldap_url->lud_scheme) {
-                               if (strcmp(ldap_url->lud_scheme, "ldaps") == 0) {
-                                       if (inst->start_tls == true) {
-                                               cf_log_err_cs(conf, "ldaps:// scheme is not compatible "
-                                                             "with 'start_tls'");
-                                               return -1;
-                                       }
-
-                                       port = inst->port ? inst->port : LDAPS_PORT;
-
-                               } else if (strcmp(ldap_url->lud_scheme, "ldapi") == 0) {
-                                       port = 0;
-
-                               } else if (strcmp(ldap_url->lud_scheme, "cldap") == 0) {
-                                       port = inst->port ? inst->port : LDAP_PORT;
-                               } /* else don't set the port */
-                       }         /* else don't set the port */
-#endif
-                       if (port < 0) port = inst->port ? inst->port : LDAP_PORT;
-
-
-                       if (ldap_url->lud_port > 0) port = ldap_url->lud_port;
+                       if (set_port_maybe) {
+                               ldap_url->lud_port = inst->port;
+                               if (!ldap_url->lud_port) ldap_url->lud_port = default_port;
+                       }
 
-#ifdef HAVE_LDAP_INITIALIZE
-                       inst->server = talloc_asprintf_append(inst->server, "%s://%s",
-                                                             ldap_url->lud_scheme ? ldap_url->lud_scheme : "ldap",
-                                                             ldap_url->lud_host ? ldap_url->lud_host : "");
-                       if (port) inst->server = talloc_asprintf_append(inst->server, ":%i", port);
-                       inst->server = talloc_strdup_append(inst->server, " ");
-#else
-                       inst->server = talloc_asprintf_append(inst->server, "%s",
-                                                             ldap_url->lud_host ? ldap_url->lud_host : "localhost");
-                       if (port) inst->server = talloc_asprintf_append(inst->server, ":%i", port);
-                       inst->server = talloc_strdup_append(inst->server, " ");
-#endif
+                       inst->server = talloc_asprintf_append(inst->server, "%s:%i ",
+                                                             ldap_url->lud_host ? ldap_url->lud_host : "localhost",
+                                                             ldap_url->lud_port);
+#  endif
                        /*
                         *      @todo We could set a few other top level
                         *      directives using the URL, like base_dn
@@ -813,7 +976,14 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                /*
                 *      We need to construct an LDAP URI
                 */
-               } else {
+               } else
+#endif /* HAVE_LDAP_URL_PARSE && HAVE_LDAP_IS_LDAP_URL && LDAP_URL_DESC2STR */
+               /*
+                *      If it's not an URL, or we don't have the functions necessary
+                *      to break apart the URL and recombine it, then just treat
+                *      server as a hostname.
+                */
+               {
 #ifdef HAVE_LDAP_INITIALIZE
                        char    const *p;
                        char    *q;
@@ -822,22 +992,32 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
                        port = inst->port;
 
+                       /*
+                        *      We don't support URLs if the library didn't provide
+                        *      URL parsing functions.
+                        */
+                       if (strchr(value, '/')) {
+                       bad_server_fmt:
+#ifdef LDAP_CAN_PARSE_URLS
+                               cf_log_err_cp(cp, "Invalid server value, must be in format <server>[:<port>] or "
+                                             "an ldap URI (ldap|cldap|ldaps|ldapi)://<server>:<port>");
+#else
+                               cf_log_err_cp(cp, "Invalid server value, must be in format <server>[:<port>]");
+#endif
+                               return -1;
+                       }
+
                        p = strrchr(value, ':');
                        if (p) {
                                port = (int)strtol((p + 1), &q, 10);
-                               if ((p == value) || ((p + 1) == q) || (*q != '\0')) {
-                                       cf_log_err_cp(cp, "Invalid server, must be in <server>[:<port>] format");
-                                       return -1;
-                               }
+                               if ((p == value) || ((p + 1) == q) || (*q != '\0')) goto bad_server_fmt;
                                len = p - value;
                        } else {
                                len = strlen(value);
                        }
-
                        if (port == 0) port = LDAP_PORT;
 
-                       inst->server = talloc_asprintf_append(inst->server, "ldap://%.*s:%i ",
-                                                             (int) len, value, port);
+                       inst->server = talloc_asprintf_append(inst->server, "ldap://%.*s:%i ", (int) len, value, port);
 #else
                        /*
                         *      ldap_init takes port, which can be overridden by :port so
@@ -926,6 +1106,37 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                goto error;
        }
 
+#ifdef HAVE_LDAP_CREATE_SORT_CONTROL
+       /*
+        *      Build the server side sort control for user objects
+        */
+       if (inst->userobj_sort_by) {
+               LDAPSortKey     **keys;
+               int             ret;
+               char            *p;
+
+               memcpy(&p, &inst->userobj_sort_by, sizeof(p));
+
+               ret = ldap_create_sort_keylist(&keys, p);
+               if (ret != LDAP_SUCCESS) {
+                       cf_log_err_cs(conf, "Invalid user.sort_by value \"%s\": %s",
+                                     inst->userobj_sort_by, ldap_err2string(ret));
+                       goto error;
+               }
+
+               /*
+                *      Always set the control as critical, if it's not needed
+                *      the user can comment it out...
+                */
+               ret = ldap_create_sort_control(inst->handle, keys, 1, &inst->userobj_sort_ctrl);
+               ldap_free_sort_keylist(keys);
+               if (ret != LDAP_SUCCESS) {
+                       LDAP_ERR("Failed creating server sort control: %s", ldap_err2string(ret));
+                       goto error;
+               }
+       }
+#endif
+
        if (inst->tls_require_cert_str) {
 #ifdef LDAP_OPT_X_TLS_NEVER
                /*
@@ -957,55 +1168,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 
        /*
-        *      Group comparison checks.
-        */
-       if (cf_section_name2(conf)) {
-               static ATTR_FLAGS flags;
-               char buffer[256];
-
-               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());
-
-                       return -1;
-               }
-               inst->group_da = dict_attrbyname(buffer);
-               if (!inst->group_da) {
-                       LDAP_ERR("Failed creating attribute %s", buffer);
-
-                       goto error;
-               }
-
-               paircompare_register(inst->group_da, dict_attrbyvalue(PW_USER_NAME, 0), false, rlm_ldap_groupcmp, inst);
-       /*
-        *      Were the default instance
-        */
-       } else {
-               inst->group_da = dict_attrbyvalue(PW_LDAP_GROUP, 0);
-               paircompare_register(dict_attrbyvalue(PW_LDAP_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0),
-                               false, rlm_ldap_groupcmp, inst);
-       }
-
-       xlat_register(inst->xlat_name, ldap_xlat, rlm_ldap_escape_func, inst);
-       xlat_register("ldapquote", ldapquote_xlat, NULL, 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());
-
-                       goto error;
-               }
-               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_module_init(inst->cs, inst, mod_conn_create, NULL, NULL);
@@ -1015,7 +1177,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
         *      Bulk load dynamic clients.
         */
        if (inst->do_clients) {
-               CONF_SECTION *cs;
+               CONF_SECTION *cs, *map, *tmpl;
 
                cs = cf_section_sub_find(inst->cs, "client");
                if (!cs) {
@@ -1023,14 +1185,16 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                        goto error;
                }
 
-               cs = cf_section_sub_find(cs, "attribute");
-               if (!cs) {
-                       cf_log_err_cs(conf, "Told to load clients but no attribute section found");
+               map = cf_section_sub_find(cs, "attribute");
+               if (!map) {
+                       cf_log_err_cs(cs, "Told to load clients but no attribute section found");
                        goto error;
                }
 
-               if (rlm_ldap_client_load(inst, cs) < 0) {
-                       cf_log_err_cs(conf, "Error loading clients");
+               tmpl = cf_section_sub_find(cs, "template");
+
+               if (rlm_ldap_client_load(inst, tmpl, map) < 0) {
+                       cf_log_err_cs(cs, "Error loading clients");
 
                        return -1;
                }
@@ -1042,14 +1206,20 @@ error:
        return -1;
 }
 
+static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull);
 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_rcode_t     rcode;
        ldap_rcode_t    status;
        char const      *dn;
-       ldap_instance_t *inst = instance;
+       rlm_ldap_t      *inst = instance;
        ldap_handle_t   *conn;
 
+       char            sasl_mech_buff[LDAP_MAX_DN_STR_LEN];
+       char            sasl_proxy_buff[LDAP_MAX_DN_STR_LEN];
+       char            sasl_realm_buff[LDAP_MAX_DN_STR_LEN];
+       ldap_sasl       sasl;
+
        /*
         * Ensure that we're being passed a plain-text password, and not
         * anything else.
@@ -1080,12 +1250,44 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                return RLM_MODULE_INVALID;
        }
 
-       RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
-
        conn = mod_conn_get(inst, request);
        if (!conn) return RLM_MODULE_FAIL;
 
        /*
+        *      Expand dynamic SASL fields
+        */
+       if (conn->inst->user_sasl.mech) {
+               memset(&sasl, 0, sizeof(sasl));
+
+               if (tmpl_expand(&sasl.mech, sasl_mech_buff, sizeof(sasl_mech_buff), request,
+                               conn->inst->user_sasl.mech, rlm_ldap_escape_func, inst) < 0) {
+                       REDEBUG("Failed expanding user.sasl.mech: %s", fr_strerror());
+                       rcode = RLM_MODULE_FAIL;
+                       goto finish;
+               }
+
+               if (conn->inst->user_sasl.proxy) {
+                       if (tmpl_expand(&sasl.proxy, sasl_proxy_buff, sizeof(sasl_proxy_buff), request,
+                                       conn->inst->user_sasl.proxy, rlm_ldap_escape_func, inst) < 0) {
+                               REDEBUG("Failed expanding user.sasl.proxy: %s", fr_strerror());
+                               rcode = RLM_MODULE_FAIL;
+                               goto finish;
+                       }
+               }
+
+               if (conn->inst->user_sasl.realm) {
+                       if (tmpl_expand(&sasl.realm, sasl_realm_buff, sizeof(sasl_realm_buff), request,
+                                       conn->inst->user_sasl.realm, rlm_ldap_escape_func, inst) < 0) {
+                               REDEBUG("Failed expanding user.sasl.realm: %s", fr_strerror());
+                               rcode = RLM_MODULE_FAIL;
+                               goto finish;
+                       }
+               }
+       }
+
+       RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue);
+
+       /*
         *      Get the DN by doing a search.
         */
        dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
@@ -1094,13 +1296,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
 
                return rcode;
        }
-
-       /*
-        *      Bind as the user
-        */
        conn->rebound = true;
        status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue,
-                              conn->inst->user_sasl_mech, true);
+                              conn->inst->user_sasl.mech ? &sasl : NULL, true);
        switch (status) {
        case LDAP_PROC_SUCCESS:
                rcode = RLM_MODULE_OK;
@@ -1128,24 +1326,97 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                break;
        };
 
+finish:
        mod_conn_release(inst, conn);
 
        return rcode;
 }
 
-static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
+/** Search for and apply an LDAP profile
+ *
+ * LDAP profiles are mapped using the same attribute map as user objects, they're used to add common sets of attributes
+ * to the request.
+ *
+ * @param[in] inst rlm_ldap configuration.
+ * @param[in] request Current request.
+ * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
+ * @param[in] dn of profile object to apply.
+ * @param[in] expanded Structure containing a list of xlat expanded attribute names and mapping information.
+ * @return One of the RLM_MODULE_* values.
+ */
+static rlm_rcode_t rlm_ldap_map_profile(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn,
+                                       char const *dn, rlm_ldap_map_exp_t const *expanded)
 {
        rlm_rcode_t     rcode = RLM_MODULE_OK;
        ldap_rcode_t    status;
+       LDAPMessage     *result = NULL, *entry = NULL;
        int             ldap_errno;
-       int             i;
-       ldap_instance_t *inst = instance;
-       struct berval   **values;
-       VALUE_PAIR      *vp;
-       ldap_handle_t   *conn;
-       LDAPMessage     *result, *entry;
-       char const      *dn = NULL;
-       rlm_ldap_map_xlat_t     expanded; /* faster than mallocing every time */
+       LDAP            *handle = (*pconn)->handle;
+       char const      *filter;
+       char            filter_buff[LDAP_MAX_FILTER_STR_LEN];
+
+       rad_assert(inst->profile_filter);       /* We always have a default filter set */
+
+       if (!dn || !*dn) return RLM_MODULE_OK;
+
+       if (tmpl_expand(&filter, filter_buff, sizeof(filter_buff), request,
+                       inst->profile_filter, rlm_ldap_escape_func, NULL) < 0) {
+               REDEBUG("Failed creating profile filter");
+
+               return RLM_MODULE_INVALID;
+       }
+
+       status = rlm_ldap_search(&result, inst, request, pconn, dn,
+                                LDAP_SCOPE_BASE, filter, expanded->attrs, NULL, NULL);
+       switch (status) {
+       case LDAP_PROC_SUCCESS:
+               break;
+
+       case LDAP_PROC_BAD_DN:
+       case LDAP_PROC_NO_RESULT:
+               RDEBUG("Profile object \"%s\" not found", dn);
+               return RLM_MODULE_NOTFOUND;
+
+       default:
+               return RLM_MODULE_FAIL;
+       }
+
+       rad_assert(*pconn);
+       rad_assert(result);
+
+       entry = ldap_first_entry(handle, result);
+       if (!entry) {
+               ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
+               REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
+
+               rcode = RLM_MODULE_NOTFOUND;
+
+               goto free_result;
+       }
+
+       RDEBUG("Processing profile attributes");
+       if (rlm_ldap_map_do(inst, request, handle, expanded, entry) > 0) rcode = RLM_MODULE_UPDATED;
+
+free_result:
+       ldap_msgfree(result);
+
+       return rcode;
+}
+
+static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) CC_HINT(nonnull);
+static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+{
+       rlm_rcode_t             rcode = RLM_MODULE_OK;
+       ldap_rcode_t            status;
+       int                     ldap_errno;
+       int                     i;
+       rlm_ldap_t              *inst = instance;
+       struct berval           **values;
+       VALUE_PAIR              *vp;
+       ldap_handle_t           *conn;
+       LDAPMessage             *result, *entry;
+       char const              *dn = NULL;
+       rlm_ldap_map_exp_t      expanded; /* faster than mallocing every time */
 
        /*
         *      Don't be tempted to add a check for request->username
@@ -1153,9 +1424,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         *      many things besides searching for users.
         */
 
-       if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) {
-               return RLM_MODULE_FAIL;
-       }
+       if (rlm_ldap_map_expand(&expanded, request, inst->user_map) < 0) return RLM_MODULE_FAIL;
 
        conn = mod_conn_get(inst, request);
        if (!conn) return RLM_MODULE_FAIL;
@@ -1225,7 +1494,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        /*
         *      We already have a Cleartext-Password.  Skip edir.
         */
-       if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
+       if (fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
                goto skip_edir;
        }
 
@@ -1251,8 +1520,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                /*
                 *      Add Cleartext-Password attribute to the request
                 */
-               vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0);
-               pairstrcpy(vp, password);
+               vp = radius_pair_create(request, &request->config, PW_CLEARTEXT_PASSWORD, 0);
+               fr_pair_value_strcpy(vp, password);
                vp->vp_length = pass_size;
 
                if (RDEBUG_ENABLED3) {
@@ -1267,8 +1536,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                         *      Bind as the user
                         */
                        conn->rebound = true;
-                       status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue,
-                                              conn->inst->user_sasl_mech, true);
+                       status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, NULL, true);
                        switch (status) {
                        case LDAP_PROC_SUCCESS:
                                rcode = RLM_MODULE_OK;
@@ -1327,8 +1595,7 @@ skip_edir:
 
                case RLM_MODULE_UPDATED:
                        rcode = RLM_MODULE_UPDATED;
-                       goto finish;
-
+                       /* FALL-THROUGH */
                default:
                        break;
                }
@@ -1341,11 +1608,18 @@ skip_edir:
                values = ldap_get_values_len(conn->handle, entry, inst->profile_attr);
                if (values != NULL) {
                        for (i = 0; values[i] != NULL; i++) {
+                               rlm_rcode_t ret;
                                char *value;
 
                                value = rlm_ldap_berval_to_string(request, values[i]);
-                               rlm_ldap_map_profile(inst, request, &conn, value, &expanded);
+                               ret = rlm_ldap_map_profile(inst, request, &conn, value, &expanded);
                                talloc_free(value);
+                               if (ret == RLM_MODULE_FAIL) {
+                                       ldap_value_free_len(values);
+                                       rcode = ret;
+                                       goto finish;
+                               }
+
                        }
                        ldap_value_free_len(values);
                }
@@ -1353,14 +1627,12 @@ skip_edir:
 
        if (inst->user_map || inst->valuepair_attr) {
                RDEBUG("Processing user attributes");
-               RINDENT();
                if (rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry) > 0) rcode = RLM_MODULE_UPDATED;
-               REXDENT();
                rlm_ldap_check_reply(inst, request);
        }
 
 finish:
-       rlm_ldap_map_xlat_free(&expanded);
+       talloc_free(expanded.ctx);
        if (result) ldap_msgfree(result);
        mod_conn_release(inst, conn);
 
@@ -1376,7 +1648,7 @@ finish:
  * @param section that holds the map to process.
  * @return one of the RLM_MODULE_* values.
  */
-static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
+static rlm_rcode_t user_modify(rlm_ldap_t *inst, REQUEST *request, ldap_acct_section_t *section)
 {
        rlm_rcode_t     rcode = RLM_MODULE_OK;
        ldap_rcode_t    status;
@@ -1599,19 +1871,20 @@ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acc
        return rcode;
 }
 
-static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * request) {
-       ldap_instance_t *inst = instance;
+static rlm_rcode_t mod_accounting(void *instance, REQUEST *request) CC_HINT(nonnull);
+static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+{
+       rlm_ldap_t *inst = instance;
 
-       if (inst->accounting) {
-               return user_modify(inst, request, inst->accounting);
-       }
+       if (inst->accounting) return user_modify(inst, request, inst->accounting);
 
        return RLM_MODULE_NOOP;
 }
 
-static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * request)
+static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull);
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
-       ldap_instance_t *inst = instance;
+       rlm_ldap_t *inst = instance;
 
        if (inst->postauth) {
                return user_modify(inst, request, inst->postauth);
@@ -1624,21 +1897,17 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * requ
 /* globally exported name */
 extern module_t rlm_ldap;
 module_t rlm_ldap = {
-       RLM_MODULE_INIT,
-       "ldap",
-       RLM_TYPE_THREAD_SAFE,   /* type: reserved        */
-       sizeof(ldap_instance_t),
-       module_config,
-       mod_instantiate,        /* instantiation         */
-       mod_detach,             /* detach                */
-       {
-               mod_authenticate,       /* authentication        */
-               mod_authorize,          /* authorization         */
-               NULL,                   /* preaccounting         */
-               mod_accounting,         /* accounting            */
-               NULL,                   /* checksimul            */
-               NULL,                   /* pre-proxy             */
-               NULL,                   /* post-proxy            */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "ldap",
+       .inst_size      = sizeof(rlm_ldap_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
diff --git a/src/modules/rlm_ldap/sasl.c b/src/modules/rlm_ldap/sasl.c
new file mode 100644 (file)
index 0000000..d71e5b1
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *   This program is 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
+ */
+
+#include "ldap.h"
+
+/**
+ * $Id$
+ * @file sasl.c
+ * @brief Functions to perform SASL binds against an LDAP directory.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2015 The FreeRADIUS Server Project.
+ */
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <sasl/sasl.h>
+
+/** Data passed to the _sasl interact callback.
+ *
+ */
+typedef struct rlm_ldap_sasl_ctx {
+       rlm_ldap_t const        *inst;          //!< LDAP instance
+       REQUEST                 *request;       //!< The current request.
+
+       char const              *identity;      //!< User's DN or identity.
+       char const              *password;      //!< Bind password.
+
+       ldap_sasl               *extra;         //!< Extra fields (realm and proxy id).
+} rlm_ldap_sasl_ctx_t;
+
+/** Callback for ldap_sasl_interactive_bind
+ *
+ * @param handle used for the SASL bind.
+ * @param flags data as provided to ldap_sasl_interactive_bind.
+ * @param ctx Our context data, containing the identity, password, realm and various other things.
+ * @param sasl_callbacks Array of challenges to provide responses for.
+ * @return SASL_OK.
+ */
+static int _sasl_interact(UNUSED LDAP *handle, UNUSED unsigned flags, void *ctx, void *sasl_callbacks)
+{
+       rlm_ldap_sasl_ctx_t     *this = ctx;
+       REQUEST                 *request = this->request;
+       rlm_ldap_t const        *inst = this->inst;
+       sasl_interact_t         *cb = sasl_callbacks;
+       sasl_interact_t         *cb_p;
+
+       for (cb_p = cb; cb_p->id != SASL_CB_LIST_END; cb_p++) {
+               MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL challenge : %s", cb_p->challenge);
+               MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL prompt    : %s", cb_p->prompt);
+
+               switch (cb_p->id) {
+               case SASL_CB_AUTHNAME:
+                       cb_p->result = this->identity;
+                       break;
+
+               case SASL_CB_PASS:
+                       cb_p->result = this->password;
+                       break;
+
+               case SASL_CB_USER:
+                       cb_p->result = this->extra->proxy ? this->extra->proxy : this->identity;
+                       break;
+
+               case SASL_CB_GETREALM:
+                       if (this->extra->realm) cb_p->result = this->extra->realm;
+                       break;
+
+               default:
+                       break;
+               }
+               MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL result    : %s", cb_p->result ? (char const *)cb_p->result : "");
+       }
+       return SASL_OK;
+}
+
+/** Initiate an LDAP interactive bind
+ *
+ * @param[in] inst rlm_ldap configuration.
+ * @param[in] request Current request, this may be NULL, in which case all debug logging is done with radlog.
+ * @param[in] conn to use. May change as this function calls functions which auto re-connect.
+ * @param[in] identity of the user.
+ * @param[in] password of the user.
+ * @param[in] sasl mechanism to use for bind, and additional parameters.
+ * @param[out] error message resulting from bind.
+ * @param[out] extra information about the error.
+ * @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
+ */
+ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request,
+                                      ldap_handle_t *conn, char const *identity,
+                                      char const *password, ldap_sasl *sasl,
+                                      char const **error, char **extra)
+{
+       ldap_rcode_t            status;
+       int                     ret = 0;
+       int                     msgid;
+       char const              *mech;
+       LDAPMessage             *result = NULL;
+       rlm_ldap_sasl_ctx_t     sasl_ctx;               /* SASL defaults */
+
+       /* rlm_ldap_result may not be called */
+       if (error) *error = NULL;
+       if (extra) *extra = NULL;
+
+       sasl_ctx.inst = inst;
+       sasl_ctx.request = request;
+       sasl_ctx.identity = identity;
+       sasl_ctx.password = password;
+       sasl_ctx.extra = sasl;
+
+       MOD_ROPTIONAL(RDEBUG2, DEBUG2, "Starting SASL mech(s): %s", sasl->mech);
+       for (;;) {
+               ret = ldap_sasl_interactive_bind(conn->handle, NULL, sasl->mech,
+                                                NULL, NULL, LDAP_SASL_AUTOMATIC,
+                                                _sasl_interact, &sasl_ctx, result,
+                                                &mech, &msgid);
+
+               /*
+                *      If ldap_sasl_interactive_bind indicates it didn't want
+                *      to continue, then we're done.
+                *
+                *      Calling ldap_result here, results in a timeout in some
+                *      cases, so we need to figure out whether the bind was
+                *      successful without the help of ldap_result.
+                */
+               if (ret != LDAP_SASL_BIND_IN_PROGRESS) {
+                       status = rlm_ldap_result(inst, conn, -1, identity, NULL, error, extra);
+                       break;          /* Old result gets freed on after exit */
+               }
+
+               ldap_msgfree(result);   /* We always need to free the old message */
+
+               /*
+                *      If LDAP parse result indicates there was an error
+                *      then we're done.
+                */
+               status = rlm_ldap_result(inst, conn, msgid, identity, &result, error, extra);
+               switch (status) {
+               case LDAP_PROC_SUCCESS:         /* ldap_sasl_interactive_bind should have indicated success */
+               case LDAP_PROC_CONTINUE:
+                       break;
+
+               default:
+                       goto done;
+               }
+
+               /*
+                *      ...otherwise, the bind is still in progress.
+                */
+               MOD_ROPTIONAL(RDEBUG3, DEBUG3, "Continuing SASL mech %s...", mech);
+
+               /*
+                *      Write the servers response to the debug log
+                */
+               if (((request && RDEBUG_ENABLED3) || DEBUG_ENABLED3) && result) {
+                       struct berval *srv_cred;
+
+                       if (ldap_parse_sasl_bind_result(conn->handle, result, &srv_cred, 0) == 0) {
+                               char *escaped;
+
+                               escaped = fr_aprints(request, srv_cred->bv_val, srv_cred->bv_len, '\0');
+                               MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL response  : %s", escaped);
+
+                               talloc_free(escaped);
+                               ldap_memfree(srv_cred);
+                       }
+               }
+       }
+done:
+       ldap_msgfree(result);
+
+       return status;
+}
index e3640e0..906b668 100644 (file)
@@ -54,8 +54,15 @@ RCSID("$Id$")
 typedef struct rlm_linelog_t {
        CONF_SECTION    *cs;
        char const      *filename;
-       char const      *syslog_facility;
-       int             facility;
+
+       bool            escape;                 //!< do filename escaping, yes / no
+
+       xlat_escape_t escape_func;      //!< escape function
+
+       char const      *syslog_facility;       //!< Syslog facility string.
+       char const      *syslog_severity;       //!< Syslog severity string.
+       int             syslog_priority;        //!< Bitwise | of severity and facility.
+
        uint32_t        permissions;
        char const      *group;
        char const      *line;
@@ -74,12 +81,14 @@ typedef struct rlm_linelog_t {
  */
 static const CONF_PARSER module_config[] = {
        { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_linelog_t, filename), NULL },
+       { "escape_filenames", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_linelog_t, escape), "no" },
        { "syslog_facility", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, syslog_facility), NULL },
+       { "syslog_severity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, syslog_severity), "info" },
        { "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 | PW_TYPE_XLAT, rlm_linelog_t, line), NULL },
        { "reference", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_linelog_t, reference), NULL },
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -89,30 +98,45 @@ static const CONF_PARSER module_config[] = {
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_linelog_t *inst = instance;
+       int num;
 
        if (!inst->filename) {
                cf_log_err_cs(conf, "No value provided for 'filename'");
                return -1;
        }
 
+       /*
+        *      Escape filenames only if asked.
+        */
+       if (inst->escape) {
+               inst->escape_func = rad_filename_escape;
+       } else {
+               inst->escape_func = rad_filename_make_safe;
+       }
+
 #ifndef HAVE_SYSLOG_H
        if (strcmp(inst->filename, "syslog") == 0) {
                cf_log_err_cs(conf, "Syslog output is not supported on this system");
                return -1;
        }
 #else
-       inst->facility = 0;
 
        if (inst->syslog_facility) {
-               inst->facility = fr_str2int(syslog_str2fac, inst->syslog_facility, -1);
-               if (inst->facility < 0) {
-                       cf_log_err_cs(conf, "Invalid syslog facility '%s'",
-                                  inst->syslog_facility);
+               num = fr_str2int(syslog_facility_table, inst->syslog_facility, -1);
+               if (num < 0) {
+                       cf_log_err_cs(conf, "Invalid syslog facility \"%s\"", inst->syslog_facility);
                        return -1;
                }
+
+               inst->syslog_priority |= num;
        }
 
-       inst->facility |= LOG_INFO;
+       num = fr_str2int(syslog_severity_table, inst->syslog_severity, -1);
+       if (num < 0) {
+               cf_log_err_cs(conf, "Invalid syslog severity \"%s\"", inst->syslog_severity);
+               return -1;
+       }
+       inst->syslog_priority |= num;
 #endif
 
        if (!inst->line && !inst->reference) {
@@ -120,7 +144,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                return -1;
        }
 
-       inst->ef = exfile_init(inst, 64, 30);
+       inst->ef = exfile_init(inst, 64, 30, true);
        if (!inst->ef) {
                cf_log_err_cs(conf, "Failed creating log file context");
                return -1;
@@ -257,7 +281,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(void *instance, REQUEST *requ
        if (strcmp(inst->filename, "syslog") != 0) {
                char path[2048];
 
-               if (radius_xlat(path, sizeof(path), request, inst->filename, rad_filename_escape, NULL) < 0) {
+               if (radius_xlat(path, sizeof(path), request, inst->filename, inst->escape_func, NULL) < 0) {
                        return RLM_MODULE_FAIL;
                }
 
@@ -317,7 +341,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(void *instance, REQUEST *requ
 
 #ifdef HAVE_SYSLOG_H
        } else {
-               syslog(inst->facility, "%s", line);
+               syslog(inst->syslog_priority, "%s", line);
 #endif
        }
 
@@ -330,25 +354,23 @@ static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(void *instance, REQUEST *requ
  */
 extern module_t rlm_linelog;
 module_t rlm_linelog = {
-       RLM_MODULE_INIT,
-       "linelog",
-       RLM_TYPE_HUP_SAFE,      /* type */
-       sizeof(rlm_linelog_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               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 */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "linelog",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_linelog_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_do_linelog,
+               [MOD_AUTHORIZE]         = mod_do_linelog,
+               [MOD_PREACCT]           = mod_do_linelog,
+               [MOD_ACCOUNTING]        = mod_do_linelog,
+               [MOD_PRE_PROXY]         = mod_do_linelog,
+               [MOD_POST_PROXY]        = mod_do_linelog,
+               [MOD_POST_AUTH]         = mod_do_linelog,
 #ifdef WITH_COA
-               , mod_do_linelog,       /* recv-coa */
-               mod_do_linelog          /* send-coa */
+               [MOD_RECV_COA]          = mod_do_linelog,
+               [MOD_SEND_COA]          = mod_do_linelog
 #endif
        },
 };
index 5b5865b..ca8249d 100644 (file)
@@ -53,10 +53,9 @@ typedef struct rlm_logintime_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-  { "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 }
+       { "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" },
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -150,7 +149,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        VALUE_PAIR *ends, *timeout;
        int left;
 
-       ends = pairfind(request->config_items, PW_LOGIN_TIME, 0, TAG_ANY);
+       ends = fr_pair_find_by_num(request->config, PW_LOGIN_TIME, 0, TAG_ANY);
        if (!ends) {
                return RLM_MODULE_NOOP;
        }
@@ -194,13 +193,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         */
        RDEBUG("Login within allowed time-slot, %d seconds left in this session", left);
 
-       timeout = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
+       timeout = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
        if (timeout) {  /* just update... */
                if (timeout->vp_integer > (unsigned int) left) {
                        timeout->vp_integer = left;
                }
        } else {
-               timeout = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
+               timeout = radius_pair_create(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
                timeout->vp_integer = left;
        }
 
@@ -238,13 +237,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        return 0;
 }
 
-static int mod_detach(UNUSED void *instance)
-{
-       paircompare_unregister(dict_attrbyvalue(PW_CURRENT_TIME, 0), timecmp);
-       paircompare_unregister(dict_attrbyvalue(PW_TIME_OF_DAY, 0), time_of_day);
-       return 0;
-}
-
 /*
  *     The module name should be the only globally exported symbol.
  *     That is, everything else should be 'static'.
@@ -256,21 +248,13 @@ static int mod_detach(UNUSED void *instance)
  */
 extern module_t rlm_logintime;
 module_t rlm_logintime = {
-       RLM_MODULE_INIT,
-       "logintime",
-       0,      /* type */
-       sizeof(rlm_logintime_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,             /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,  /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_authorize           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "logintime",
+       .inst_size      = sizeof(rlm_logintime_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_POST_AUTH]         = mod_authorize
        },
 };
index 8f935ea..a1fffc6 100644 (file)
@@ -177,10 +177,9 @@ static int day_fill(char *bitmap, char const *tm)
 static int week_fill(char *bitmap, char const *tm)
 {
        char *s;
-       char tmp[128];
+       char tmp[256];
 
-       strlcpy(tmp, tm, 128);
-       tmp[127] = 0;
+       strlcpy(tmp, tm, sizeof(tmp));
        for (s = tmp; *s; s++)
                if (isupper(*s)) *s = tolower(*s);
 
diff --git a/src/modules/rlm_mschap/auth_wbclient.c b/src/modules/rlm_mschap/auth_wbclient.c
new file mode 100644 (file)
index 0000000..304eff4
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ *   This program is 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$
+ * @file auth_wbclient.c
+ * @brief NTLM authentication against the wbclient library
+ *
+ * @copyright 2015  Matthew Newton
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <core/ntstatus.h>
+
+#include "rlm_mschap.h"
+#include "mschap.h"
+#include "auth_wbclient.h"
+
+#define NT_LENGTH 24
+
+/*
+ *     Check NTLM authentication direct to winbind via
+ *     Samba's libwbclient library
+ *
+ *     Returns:
+ *      0    success
+ *      -1   auth failure
+ *      -648 password expired
+ */
+int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request,
+                    uint8_t const *challenge, uint8_t const *response,
+                    uint8_t nthashhash[NT_DIGEST_LENGTH])
+{
+       int rcode = -1;
+       struct wbcContext *wb_ctx;
+       struct wbcAuthUserParams authparams;
+       wbcErr err;
+       int len;
+       struct wbcAuthUserInfo *info = NULL;
+       struct wbcAuthErrorInfo *error = NULL;
+       char user_name_buf[500];
+       char domain_name_buf[500];
+       uint8_t resp[NT_LENGTH];
+
+       /*
+        * Clear the auth parameters - this is important, as
+        * there are options that will cause wbcAuthenticateUserEx
+        * to bomb out if not zero.
+        */
+       memset(&authparams, 0, sizeof(authparams));
+
+       /*
+        * wb_username must be set for this function to be called
+        */
+       rad_assert(inst->wb_username);
+
+       /*
+        * Get the username and domain from the configuration
+        */
+       len = tmpl_expand(&authparams.account_name, user_name_buf, sizeof(user_name_buf),
+                         request, inst->wb_username, NULL, NULL);
+       if (len < 0) {
+               REDEBUG2("Unable to expand winbind_username");
+               goto done;
+       }
+
+       if (inst->wb_domain) {
+               len = tmpl_expand(&authparams.domain_name, domain_name_buf, sizeof(domain_name_buf),
+                                 request, inst->wb_domain, NULL, NULL);
+               if (len < 0) {
+                       REDEBUG2("Unable to expand winbind_domain");
+                       goto done;
+               }
+       } else {
+               RWDEBUG2("No domain specified; authentication may fail because of this");
+       }
+
+
+       /*
+        * Build the wbcAuthUserParams structure with what we know
+        */
+       authparams.level = WBC_AUTH_USER_LEVEL_RESPONSE;
+       authparams.password.response.nt_length = NT_LENGTH;
+
+       memcpy(resp, response, NT_LENGTH);
+       authparams.password.response.nt_data = resp;
+
+       memcpy(authparams.password.response.challenge, challenge,
+              sizeof(authparams.password.response.challenge));
+
+
+       /*
+        * Send auth request across to winbind
+        */
+       wb_ctx = fr_connection_get(inst->wb_pool);
+       if (wb_ctx == NULL) {
+               RERROR("Unable to get winbind connection from pool");
+               goto done;
+       }
+
+       RDEBUG2("sending authentication request user='%s' domain='%s'", authparams.account_name,
+                                                                       authparams.domain_name);
+
+       err = wbcCtxAuthenticateUserEx(wb_ctx, &authparams, &info, &error);
+
+       fr_connection_release(inst->wb_pool, wb_ctx);
+
+
+       /*
+        * Try and give some useful feedback on what happened. There are only
+        * a few errors that can actually be returned from wbcCtxAuthenticateUserEx.
+        */
+       switch (err) {
+       case WBC_ERR_SUCCESS:
+               rcode = 0;
+               RDEBUG2("Authenticated successfully");
+               /* Grab the nthashhash from the result */
+               memcpy(nthashhash, info->user_session_key, NT_DIGEST_LENGTH);
+               break;
+       case WBC_ERR_WINBIND_NOT_AVAILABLE:
+               RERROR("Unable to contact winbind!");
+               RDEBUG2("Check that winbind is running and that FreeRADIUS has");
+               RDEBUG2("permission to connect to the winbind privileged socket.");
+               break;
+       case WBC_ERR_DOMAIN_NOT_FOUND:
+               REDEBUG2("Domain not found");
+               break;
+       case WBC_ERR_AUTH_ERROR:
+               if (!error) {
+                       REDEBUG2("Authentication failed");
+                       break;
+               }
+
+               /*
+                * The password needs to be changed, so set rcode appropriately.
+                */
+               if (error->nt_status & NT_STATUS_PASSWORD_EXPIRED ||
+                   error->nt_status & NT_STATUS_PASSWORD_MUST_CHANGE) {
+                       rcode = -648;
+               }
+
+               /*
+                * Return the NT_STATUS human readable error string, if there is one.
+                */
+               if (error->display_string) {
+                       REDEBUG2("%s [0x%X]", error->display_string, error->nt_status);
+               } else {
+                       REDEBUG2("Authentication failed [0x%X]", error->nt_status);
+               }
+               break;
+       default:
+               /*
+                * Only errors left are 
+                *   WBC_ERR_INVALID_PARAM
+                *   WBC_ERR_NO_MEMORY
+                * neither of which are particularly likely.
+                */
+               if (error && error->display_string) {
+                       REDEBUG2("libwbclient error: wbcErr %d (%s)", err, error->display_string);
+               } else {
+                       REDEBUG2("libwbclient error: wbcErr %d", err);
+               }
+               break;
+       }
+
+
+done:
+       if (info) wbcFreeMemory(info);
+       if (error) wbcFreeMemory(error);
+
+       return rcode;
+}
+
diff --git a/src/modules/rlm_mschap/auth_wbclient.h b/src/modules/rlm_mschap/auth_wbclient.h
new file mode 100644 (file)
index 0000000..83ebd20
--- /dev/null
@@ -0,0 +1,14 @@
+/* Copyright 2015 The FreeRADIUS server project */
+
+#ifndef _AUTH_WBCLIENT_H
+#define _AUTH_WBCLIENT_H
+
+RCSIDH(auth_wbclient_h, "$Id$")
+
+#include <wbclient.h>
+
+int do_auth_wbclient(rlm_mschap_t *inst, REQUEST *request,
+                    uint8_t const *challenge, uint8_t const *response,
+                    uint8_t nthashhash[NT_DIGEST_LENGTH]);
+
+#endif /*_AUTH_WBCLIENT_H*/
index 048da45..83f3b78 100644 (file)
@@ -1,7 +1,7 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
 /* Build with Apple Open Directory support */
-#undef HAVE_OPEN_DIRECTORY
+#undef HAVE_MEMBERSHIP_H
 
 /* Define to the address where bug reports for this package should be sent. */
 #undef PACKAGE_BUGREPORT
@@ -20,3 +20,6 @@
 
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
+
+/* Build with direct winbind auth support */
+#undef WITH_AUTH_WINBIND
index 2e11566..5bd039c 100755 (executable)
@@ -638,6 +638,9 @@ SHELL'
 ac_subst_files=''
 ac_user_opts='
 enable_option_checking
+with_winbind_include_dir
+with_winbind_lib_dir
+with_winbind_dir
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1251,6 +1254,15 @@ 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-winbind-include-dir=DIR
+                          Directory where the winbind includes may be found
+  --with-winbind-lib-dir=DIR
+                          Directory where the winbind libraries may be found
+  --with-winbind-dir=DIR  Base directory where winbind is installed
+
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
@@ -1415,6 +1427,52 @@ fi
   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.
@@ -1772,7 +1830,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 if test x$with_rlm_mschap != xno; then
 
-       ac_ext=c
+    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'
@@ -2561,7 +2619,7 @@ 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_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'
@@ -2700,6 +2758,61 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
+        winbind_include_dir=
+
+# Check whether --with-winbind-include-dir was given.
+if test "${with_winbind_include_dir+set}" = set; then :
+  withval=$with_winbind_include_dir; case "$withval" in
+           no)
+               as_fn_error $? "Need winbind-include-dir" "$LINENO" 5
+               ;;
+           yes)
+               ;;
+           *)
+               winbind_include_dir="$withval"
+               ;;
+       esac
+fi
+
+
+        winbind_lib_dir=
+
+# Check whether --with-winbind-lib-dir was given.
+if test "${with_winbind_lib_dir+set}" = set; then :
+  withval=$with_winbind_lib_dir; case "$withval" in
+           no)
+               as_fn_error $? "Need winbind-lib-dir" "$LINENO" 5
+               ;;
+           yes)
+               ;;
+           *)
+               winbind_lib_dir="$withval"
+               ;;
+       esac
+fi
+
+
+
+# Check whether --with-winbind-dir was given.
+if test "${with_winbind_dir+set}" = set; then :
+  withval=$with_winbind_dir; case "$withval" in
+           no)
+               as_fn_error $? "Need winbind-dir" "$LINENO" 5
+               ;;
+           yes)
+               ;;
+           *)
+               winbind_lib_dir="$withval/lib"
+               winbind_include_dir="$withval/include"
+               ;;
+       esac
+fi
+
+
+
+
+    mschap_sources=
+
 
 
 ac_safe=`echo "membership.h" | sed 'y%./+-%__pm%'`
 
 smart_prefix=
 
-       if test "x$ac_cv_header_membership_h" = "xyes"; then
+    if test "x$ac_cv_header_membership_h" = "xyes"; then
 
 $as_echo "#define HAVE_MEMBERSHIP_H 1" >>confdefs.h
 
-               mschap_sources="opendir.c"
-               mod_ldflags="-framework DirectoryService"
-       fi
-       targetname=rlm_mschap
+        mschap_sources="$mschap_sources opendir.c"
+        mod_ldflags="-framework DirectoryService"
+    fi
+
+    smart_try_dir="$winbind_include_dir"
+
+
+ac_safe=`echo "wbclient.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir="/usr/local/include /opt/include"
+
+_smart_try_dir=
+_smart_include_dir=
+
+for _prefix in $smart_prefix ""; do
+  for _dir in $smart_try_dir; do
+    _smart_try_dir="${_smart_try_dir} ${_dir}/${_prefix}"
+  done
+
+  for _dir in $smart_include_dir; do
+    _smart_include_dir="${_smart_include_dir} ${_dir}/${_prefix}"
+  done
+done
+
+if test "x$_smart_try_dir" != "x"; then
+  for try in $_smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for wbclient.h in $try" >&5
+$as_echo_n "checking for wbclient.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+                                       #include <stdbool.h>
+                   #include <wbclient.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
+  for _prefix in $smart_prefix; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/wbclient.h" >&5
+$as_echo_n "checking for ${_prefix}/wbclient.h... " >&6; }
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+                                       #include <stdbool.h>
+                   #include <wbclient.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem ${_prefix}/"
+                    { $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
+fi
+
+if test "x$smart_include" = "x"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for wbclient.h" >&5
+$as_echo_n "checking for wbclient.h... " >&6; }
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+                                       #include <stdbool.h>
+                   #include <wbclient.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
+
+  for prefix in $smart_prefix; do
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file="${_prefix}/${1}"
+
+  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\""
+
+  done
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=wbclient.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; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for wbclient.h in $try" >&5
+$as_echo_n "checking for wbclient.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdint.h>
+                                       #include <stdbool.h>
+                   #include <wbclient.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
+
+smart_prefix=
+
+    if test "x$ac_cv_header_wbclient_h" != "xyes"; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: wbclient.h not found. Use --with-winbind-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: wbclient.h not found. Use --with-winbind-include-dir=<path>." >&2;}
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently building without support for direct authentication via winbind. requires: libwbclient" >&5
+$as_echo "$as_me: WARNING: silently building without support for direct authentication via winbind. requires: libwbclient" >&2;}
+    fi
+
+
+    smart_try_dir="$winbind_lib_dir"
+
+
+sm_lib_safe=`echo "wbclient" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "wbcCtxAuthenticateUserEx" | 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 wbcCtxAuthenticateUserEx in -lwbclient in $try" >&5
+$as_echo_n "checking for wbcCtxAuthenticateUserEx in -lwbclient in $try... " >&6; }
+    LIBS="-lwbclient $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char wbcCtxAuthenticateUserEx();
+int
+main ()
+{
+wbcCtxAuthenticateUserEx()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-lwbclient"
+                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 wbcCtxAuthenticateUserEx in -lwbclient" >&5
+$as_echo_n "checking for wbcCtxAuthenticateUserEx in -lwbclient... " >&6; }
+  LIBS="-lwbclient $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char wbcCtxAuthenticateUserEx();
+int
+main ()
+{
+wbcCtxAuthenticateUserEx()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-lwbclient"
+               { $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=libwbclient${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=libwbclient.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 wbcCtxAuthenticateUserEx in -lwbclient in $try" >&5
+$as_echo_n "checking for wbcCtxAuthenticateUserEx in -lwbclient in $try... " >&6; }
+    LIBS="-lwbclient $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char wbcCtxAuthenticateUserEx();
+int
+main ()
+{
+wbcCtxAuthenticateUserEx()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-lwbclient"
+                 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_wbclient_wbcCtxAuthenticateUserEx" != "xyes"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: winbind libraries not found. Use --with-winbind-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: winbind libraries not found. Use --with-winbind-lib-dir=<path>." >&2;}
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Samba must be version 4.2.1 or higher to use this feature." >&5
+$as_echo "$as_me: WARNING: Samba must be version 4.2.1 or higher to use this feature." >&2;}
+    elif test "x$ac_cv_header_wbclient_h" = "xyes"; then
+      mschap_sources="$mschap_sources auth_wbclient.c"
+
+$as_echo "#define WITH_AUTH_WINBIND 1" >>confdefs.h
+
+    fi
+
+    targetname=rlm_mschap
 else
-       targetname=
-       echo \*\*\* module rlm_mschap is disabled.
+    targetname=
+    echo \*\*\* module rlm_mschap is disabled.
 fi
 
 if test x"$fail" != x""; then
@@ -2951,6 +3491,8 @@ $as_echo "$as_me: WARNING: FAILURE: rlm_mschap requires: $fail." >&2;};
        fi
 fi
 
+mod_ldflags="$mod_ldflags $SMART_LIBS"
+mod_cflags="$SMART_CPPFLAGS"
 
 
 
index c001c4e..41b5a56 100644 (file)
@@ -4,19 +4,100 @@ AC_DEFUN(modname,[rlm_mschap])
 
 if test x$with_[]modname != xno; then
 
-       AC_PROG_CC
-       AC_PROG_CPP
-
-       FR_SMART_CHECK_INCLUDE(membership.h)
-       if test "x$ac_cv_header_membership_h" = "xyes"; then
-               AC_DEFINE([HAVE_MEMBERSHIP_H],[1],[Build with Apple Open Directory support])
-               mschap_sources="opendir.c"
-               mod_ldflags="-framework DirectoryService"
-       fi
-       targetname=modname
+    AC_PROG_CC
+    AC_PROG_CPP
+
+    dnl ############################################################
+    dnl # Check for command line options
+    dnl ############################################################
+
+    dnl extra argument: --with-winbind-include-dir=DIR
+    winbind_include_dir=
+    AC_ARG_WITH(winbind-include-dir,
+       [AS_HELP_STRING([--with-winbind-include-dir=DIR],
+               [Directory where the winbind includes may be found])],
+       [case "$withval" in
+           no)
+               AC_MSG_ERROR(Need winbind-include-dir)
+               ;;
+           yes)
+               ;;
+           *)
+               winbind_include_dir="$withval"
+               ;;
+       esac])
+
+    dnl extra argument: --with-winbind-lib-dir=DIR
+    winbind_lib_dir=
+    AC_ARG_WITH(winbind-lib-dir,
+       [AS_HELP_STRING([--with-winbind-lib-dir=DIR],
+               [Directory where the winbind libraries may be found])],
+       [case "$withval" in
+           no)
+               AC_MSG_ERROR(Need winbind-lib-dir)
+               ;;
+           yes)
+               ;;
+           *)
+               winbind_lib_dir="$withval"
+               ;;
+       esac])
+
+    dnl extra argument: --with-winbind-dir=DIR
+    AC_ARG_WITH(winbind-dir,
+       [AS_HELP_STRING([--with-winbind-dir=DIR],
+               [Base directory where winbind is installed])],
+       [case "$withval" in
+           no)
+               AC_MSG_ERROR(Need winbind-dir)
+               ;;
+           yes)
+               ;;
+           *)
+               winbind_lib_dir="$withval/lib"
+               winbind_include_dir="$withval/include"
+               ;;
+       esac])
+
+
+    dnl ############################################################
+    dnl # Check for header files
+    dnl ############################################################
+
+    mschap_sources=
+    FR_SMART_CHECK_INCLUDE(membership.h)
+    if test "x$ac_cv_header_membership_h" = "xyes"; then
+        AC_DEFINE([HAVE_MEMBERSHIP_H],[1],[Build with Apple Open Directory support])
+        mschap_sources="$mschap_sources opendir.c"
+        mod_ldflags="-framework DirectoryService"
+    fi
+
+    smart_try_dir="$winbind_include_dir"
+    FR_SMART_CHECK_INCLUDE(wbclient.h, [#include <stdint.h>
+                                       #include <stdbool.h>])
+    if test "x$ac_cv_header_wbclient_h" != "xyes"; then
+        AC_MSG_WARN([wbclient.h not found. Use --with-winbind-include-dir=<path>.])
+        AC_MSG_WARN([silently building without support for direct authentication via winbind. requires: libwbclient])
+    fi
+
+    dnl ############################################################
+    dnl # Check for libraries
+    dnl ############################################################
+
+    smart_try_dir="$winbind_lib_dir"
+    FR_SMART_CHECK_LIB(wbclient, wbcCtxAuthenticateUserEx)
+    if test "x$ac_cv_lib_wbclient_wbcCtxAuthenticateUserEx" != "xyes"; then
+      AC_MSG_WARN([winbind libraries not found. Use --with-winbind-lib-dir=<path>.])
+      AC_MSG_WARN([Samba must be version 4.2.1 or higher to use this feature.])
+    elif test "x$ac_cv_header_wbclient_h" = "xyes"; then
+      mschap_sources="$mschap_sources auth_wbclient.c"
+      AC_DEFINE([WITH_AUTH_WINBIND],[1],[Build with direct winbind auth support])
+    fi
+
+    targetname=modname
 else
-       targetname=
-       echo \*\*\* module modname is disabled.
+    targetname=
+    echo \*\*\* module modname is disabled.
 fi
 
 if test x"$fail" != x""; then
@@ -29,6 +110,8 @@ if test x"$fail" != x""; then
        fi
 fi
 
+mod_ldflags="$mod_ldflags $SMART_LIBS"
+mod_cflags="$SMART_CPPFLAGS"
 AC_SUBST(mschap_sources)
 AC_SUBST(mod_ldflags)
 AC_SUBST(mod_cflags)
index b4236e0..4e088ed 100644 (file)
@@ -71,7 +71,7 @@ void mschap_challenge_hash(uint8_t const *peer_challenge,
                            uint8_t const *auth_challenge,
                            char const *user_name, uint8_t *challenge )
 {
-       fr_SHA1_CTX Context;
+       fr_sha1_ctx Context;
        uint8_t hash[20];
 
        fr_sha1_init(&Context);
@@ -94,7 +94,7 @@ void mschap_auth_response(char const *username,
                          uint8_t const *peer_challenge, uint8_t const *auth_challenge,
                          char *response)
 {
-       fr_SHA1_CTX Context;
+       fr_sha1_ctx Context;
        static const uint8_t magic1[39] =
        {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
         0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
index ccd883b..1b37811 100644 (file)
@@ -49,10 +49,12 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
 static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **outUserName,
                                  tDirNodeReference* userNodeRef, tDirReference dsRef)
 {
-       tDataBuffer          *tDataBuff = NULL;
+       tDataBuffer             *tDataBuff      = NULL;
        tDirNodeReference       nodeRef         = 0;
-       long                status              = eDSNoErr;
-       tContextData        context             = 0;
+       long                    status          = eDSNoErr;
+       char const              *what           = NULL;
+       char                    *status_name    = NULL;
+       tContextData            context         = 0;
        uint32_t                nodeCount       = 0;
        uint32_t                attrIndex       = 0;
        tDataList              *nodeName        = NULL;
@@ -61,9 +63,9 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
        tDataList              *pRecType        = NULL;
        tDataList              *pAttrType       = NULL;
        uint32_t                recCount        = 0;
-       tRecordEntry        *pRecEntry  = NULL;
+       tRecordEntry            *pRecEntry      = NULL;
        tAttributeListRef       attrListRef     = 0;
-       char                *pUserLocation      = NULL;
+       char                    *pUserLocation  = NULL;
        tAttributeValueListRef  valueRef        = 0;
        tDataList              *pUserNode       = NULL;
        rlm_rcode_t             result          = RLM_MODULE_FAIL;
@@ -75,7 +77,7 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
 
        tDataBuff = dsDataBufferAllocate(dsRef, 4096);
        if (!tDataBuff) {
-               ERROR("rlm_mschap: getUserNodeRef(): dsDataBufferAllocate() status = %ld", status);
+               RERROR("Failed allocating buffer");
                return RLM_MODULE_FAIL;
        }
 
@@ -84,34 +86,27 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
                status = dsFindDirNodes(dsRef, tDataBuff, NULL,
                                        eDSAuthenticationSearchNodeName,
                                        &nodeCount, &context);
-               if (status != eDSNoErr) {
-                       ERROR("rlm_mschap: getUserNodeRef(): no node found? status = %ld", status);
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
+#define OPEN_DIR_ERROR(_x) do if (status != eDSNoErr) { \
+                               what = _x; \
+                               goto error; \
+                       } while (0)
+
+               OPEN_DIR_ERROR("Failed to find directory");
+
                if (nodeCount < 1) {
-                       ERROR("rlm_mschap: getUserNodeRef(): nodeCount < 1, status = %ld", status);
-                       result = RLM_MODULE_FAIL;
-                       break;
+                       what = "No directories found.";
+                       goto error;
                }
 
                status = dsGetDirNodeName(dsRef, tDataBuff, 1, &nodeName);
-               if (status != eDSNoErr) {
-                       ERROR("rlm_mschap: getUserNodeRef(): dsGetDirNodeName() status = %ld", status);
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
+               OPEN_DIR_ERROR("Failed getting directory name");
 
                status = dsOpenDirNode(dsRef, nodeName, &nodeRef);
                dsDataListDeallocate(dsRef, nodeName);
                free(nodeName);
                nodeName = NULL;
 
-               if (status != eDSNoErr) {
-                       ERROR("rlm_mschap: getUserNodeRef(): dsOpenDirNode() status = %ld", status);
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
+               OPEN_DIR_ERROR("Failed opening directory");
 
                pRecName = dsBuildListFromStrings(dsRef, inUserName, NULL);
                pRecType = dsBuildListFromStrings(dsRef, kDSStdRecordTypeUsers,
@@ -124,19 +119,16 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
                status = dsGetRecordList(nodeRef, tDataBuff, pRecName,
                                         eDSExact, pRecType, pAttrType, 0,
                                         &recCount, &context);
-               if (status != eDSNoErr || recCount == 0) {
-                       ERROR("rlm_mschap: getUserNodeRef(): dsGetRecordList() status = %ld, recCount=%u", status, recCount);
-                       result = RLM_MODULE_FAIL;
-                       break;
+               OPEN_DIR_ERROR("Failed getting record list");
+
+               if (recCount == 0) {
+                       what = "No user records returned";
+                       goto error;
                }
 
                status = dsGetRecordEntry(nodeRef, tDataBuff, 1,
                                          &attrListRef, &pRecEntry);
-               if (status != eDSNoErr) {
-                       ERROR("rlm_mschap: getUserNodeRef(): dsGetRecordEntry() status = %ld", status);
-                       result = RLM_MODULE_FAIL;
-                       break;
-               }
+               OPEN_DIR_ERROR("Failed getting record entry");
 
                for (attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++) {
                        status = dsGetAttributeEntry(nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry);
@@ -187,7 +179,7 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
 
                pUserNode = dsBuildFromPath(dsRef, pUserLocation, "/");
                if (!pUserNode) {
-                       ERROR("rlm_mschap: getUserNodeRef(): dsBuildFromPath() returned NULL");
+                       RERROR("Failed building user from path");
                        result = RLM_MODULE_FAIL;
                        break;
                }
@@ -197,7 +189,10 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
                free(pUserNode);
 
                if (status != eDSNoErr) {
-                       ERROR("rlm_mschap: getUserNodeRef(): dsOpenDirNode() status = %ld", status);
+               error:
+                       status_name = dsCopyDirStatusName(status);
+                       RERROR("%s: status = %s", what, status_name);
+                       free(status_name);
                        result = RLM_MODULE_FAIL;
                        break;
                }
@@ -246,7 +241,7 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
        uint32_t                uiLen            = 0;
        char                    *username_string = NULL;
        char                    *shortUserName   = NULL;
-       VALUE_PAIR              *response        = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
+       VALUE_PAIR              *response        = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
 #ifndef NDEBUG
        unsigned int t;
 #endif
@@ -260,7 +255,7 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
        status = dsOpenDirService(&dsRef);
        if (status != eDSNoErr) {
                talloc_free(username_string);
-               ERROR("rlm_mschap: od_mschap_auth(): dsOpenDirService = %d", status);
+               RERROR("Failed opening directory service");
                return RLM_MODULE_FAIL;
        }
 
@@ -410,8 +405,9 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
                dsCloseDirService(dsRef);
 
        if (status != eDSNoErr) {
-               errno = EACCES;
-               ERROR("rlm_mschap: authentication failed %d", status); /* <-- returns -14091 (eDSAuthMethodNotSupported) -14090 */
+               char *status_name = dsCopyDirStatusName(status);
+               RERROR("rlm_mschap: authentication failed - status = %s", status_name);
+               free(status_name);
                return RLM_MODULE_REJECT;
        }
 
index 49647f2..3b17240 100644 (file)
 /*  MPPE support from Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp> */
 RCSID("$Id$")
 
-#include       <freeradius-devel/radiusd.h>
-#include       <freeradius-devel/modules.h>
-#include       <freeradius-devel/rad_assert.h>
-#include       <freeradius-devel/md5.h>
-#include       <freeradius-devel/sha1.h>
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/md5.h>
+#include <freeradius-devel/sha1.h>
 
-#include       <ctype.h>
+#include <ctype.h>
 
-#include       "mschap.h"
-#include       "smbdes.h"
+#include "rlm_mschap.h"
+#include "mschap.h"
+#include "smbdes.h"
+
+#ifdef WITH_AUTH_WINBIND
+#include "auth_wbclient.h"
+#endif
 
 #ifdef HAVE_OPENSSL_CRYPTO_H
 USES_APPLE_DEPRECATED_API      /* OpenSSL API has been deprecated by Apple */
 #  include     <openssl/rc4.h>
 #endif
 
-#ifdef WITH_OPEN_DIRECTORY
+#ifdef __APPLE__
 int od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR * usernamepair);
 #endif
 
@@ -138,27 +143,6 @@ static int pdb_decode_acct_ctrl(char const *p)
 }
 
 
-typedef struct rlm_mschap_t {
-       bool            use_mppe;
-       bool            require_encryption;
-       bool            require_strong;
-       bool            with_ntdomain_hack;     /* this should be in another module */
-       char const      *xlat_name;
-       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 const      *retry_msg;
-#ifdef WITH_OPEN_DIRECTORY
-       bool            open_directory;
-#endif
-} rlm_mschap_t;
-
-
 /*
  *     Does dynamic translation of strings.
  *
@@ -182,7 +166,7 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
         *      hash of MS-CHAPv2 challenge, and peer challenge.
         */
        if (strncasecmp(fmt, "Challenge", 9) == 0) {
-               chap_challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
+               chap_challenge = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
                if (!chap_challenge) {
                        REDEBUG("No MS-CHAP-Challenge in the request");
                        return -1;
@@ -205,7 +189,7 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                        VALUE_PAIR *name_attr, *response_name;
                        char const *username_string;
 
-                       response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
+                       response = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
                        if (!response) {
                                REDEBUG("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge");
                                return -1;
@@ -225,7 +209,7 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                                return -1;
                        }
 
-                       user_name = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+                       user_name = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                        if (!user_name) {
                                REDEBUG("User-Name is required to calculate MS-CHAPv1 Challenge");
                                return -1;
@@ -240,7 +224,7 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                         *      We prefer this to the User-Name in the
                         *      packet.
                         */
-                       response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME, 0, TAG_ANY);
+                       response_name = fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_USER_NAME, 0, TAG_ANY);
                        if (response_name) {
                                name_attr = response_name;
                        } else {
@@ -285,13 +269,13 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                        return -1;
                }
 
-               /*
-                *      Get the MS-CHAPv1 response, or the MS-CHAPv2
-                *      response.
-                */
+       /*
+        *      Get the MS-CHAPv1 response, or the MS-CHAPv2
+        *      response.
+        */
        } else if (strncasecmp(fmt, "NT-Response", 11) == 0) {
-               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);
+               response = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
+               if (!response) response = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
                if (!response) {
                        REDEBUG("No MS-CHAP-Response or MS-CHAP2-Response was found in the request");
                        return -1;
@@ -316,12 +300,12 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                data = response->vp_octets + 26;
                data_len = 24;
 
-               /*
-                *      LM-Response is deprecated, and exists only
-                *      in MS-CHAPv1, and not often there.
-                */
+       /*
+        *      LM-Response is deprecated, and exists only
+        *      in MS-CHAPv1, and not often there.
+        */
        } else if (strncasecmp(fmt, "LM-Response", 11) == 0) {
-               response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
+               response = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
                if (!response) {
                        REDEBUG("No MS-CHAP-Response was found in the request");
                        return -1;
@@ -338,13 +322,13 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                data = response->vp_octets + 2;
                data_len = 24;
 
-               /*
-                *      Pull the NT-Domain out of the User-Name, if it exists.
-                */
+       /*
+        *      Pull the NT-Domain out of the User-Name, if it exists.
+        */
        } else if (strncasecmp(fmt, "NT-Domain", 9) == 0) {
                char *p, *q;
 
-               user_name = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               user_name = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!user_name) {
                        REDEBUG("No User-Name was found in the request");
                        return -1;
@@ -393,13 +377,13 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
 
                return strlen(out);
 
-               /*
-                *      Pull the User-Name out of the User-Name...
-                */
+       /*
+        *      Pull the User-Name out of the User-Name...
+        */
        } else if (strncasecmp(fmt, "User-Name", 9) == 0) {
                char const *p, *q;
 
-               user_name = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               user_name = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!user_name) {
                        REDEBUG("No User-Name was found in the request");
                        return -1;
@@ -445,9 +429,9 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
 
                return strlen(out);
 
-               /*
-                * Return the NT-Hash of the passed string
-                */
+       /*
+        * Return the NT-Hash of the passed string
+        */
        } else if (strncasecmp(fmt, "NT-Hash ", 8) == 0) {
                char const *p;
 
@@ -468,9 +452,9 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                RDEBUG("NT-Hash of \"known-good\" password: %s", out);
                return 32;
 
-               /*
-                * Return the LM-Hash of the passed string
-                */
+       /*
+        * Return the LM-Hash of the passed string
+        */
        } else if (strncasecmp(fmt, "LM-Hash ", 8) == 0) {
                char const *p;
 
@@ -519,12 +503,46 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
 }
 
 
+#ifdef WITH_AUTH_WINBIND
+/*
+ *     Free connection pool winbind context
+ */
+static int _mod_conn_free(struct wbcContext **wb_ctx)
+{
+       wbcCtxFree(*wb_ctx);
+
+       return 0;
+}
+
+/*
+ *     Create connection pool winbind context
+ */
+static void *mod_conn_create(TALLOC_CTX *ctx, UNUSED void *instance)
+{
+       struct wbcContext **wb_ctx;
+
+       wb_ctx = talloc_zero(ctx, struct wbcContext *);
+       *wb_ctx = wbcCtxCreate();
+
+       if (*wb_ctx == NULL) {
+               ERROR("failed to create winbind context");
+               talloc_free(wb_ctx);
+               return NULL;
+       }
+
+       talloc_set_destructor(wb_ctx, _mod_conn_free);
+
+       return *wb_ctx;
+}
+#endif
+
+
 static const CONF_PARSER passchange_config[] = {
        { "ntlm_auth", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_mschap_t, ntlm_cpw), NULL },
        { "ntlm_auth_username", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_mschap_t, ntlm_cpw_username), NULL },
        { "ntlm_auth_domain", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_mschap_t, ntlm_cpw_domain), NULL },
        { "local_cpw", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_mschap_t, local_cpw), NULL },
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER module_config[] = {
@@ -540,19 +558,16 @@ static const CONF_PARSER module_config[] = {
        { "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
+       { "winbind_username", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_mschap_t, wb_username), NULL },
+       { "winbind_domain", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_TMPL, rlm_mschap_t, wb_domain), NULL },
+#ifdef __APPLE__
        { "use_open_directory", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, open_directory), "yes" },
 #endif
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
-/*
- *     Create instance for our module. Allocate space for
- *     instance structure and read configuration parameters
- */
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        char const *name;
        rlm_mschap_t *inst = instance;
@@ -565,6 +580,17 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        inst->xlat_name = name;
        xlat_register(inst->xlat_name, mschap_xlat, NULL, inst);
 
+       return 0;
+}
+
+/*
+ *     Create instance for our module. Allocate space for
+ *     instance structure and read configuration parameters
+ */
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+       rlm_mschap_t *inst = instance;
+
        /*
         *      For backwards compatibility
         */
@@ -575,6 +601,45 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 
        /*
+        *      Set auth method
+        */
+       inst->method = AUTH_INTERNAL;
+
+       if (inst->wb_username) {
+#ifdef WITH_AUTH_WINBIND
+               inst->method = AUTH_WBCLIENT;
+
+               inst->wb_pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, NULL);
+               if (!inst->wb_pool) {
+                       cf_log_err_cs(conf, "Unable to initialise winbind connection pool");
+                       return -1;
+               }
+#else
+               cf_log_err_cs(conf, "'winbind' auth not enabled at compiled time");
+               return -1;
+#endif
+       }
+
+       /* preserve existing behaviour: this option overrides all */
+       if (inst->ntlm_auth) {
+               inst->method = AUTH_NTLMAUTH_EXEC;
+       }
+
+       switch (inst->method) {
+       case AUTH_INTERNAL:
+               DEBUG("rlm_mschap (%s): using internal authentication", inst->xlat_name);
+               break;
+       case AUTH_NTLMAUTH_EXEC:
+               DEBUG("rlm_mschap (%s): authenticating by calling 'ntlm_auth'", inst->xlat_name);
+               break;
+#ifdef WITH_AUTH_WINBIND
+       case AUTH_WBCLIENT:
+               DEBUG("rlm_mschap (%s): authenticating directly to winbind", inst->xlat_name);
+               break;
+#endif
+       }
+
+       /*
         *      Check ntlm_auth_timeout is sane
         */
        if (!inst->ntlm_auth_timeout) {
@@ -595,6 +660,20 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 }
 
 /*
+ *     Tidy up instance
+ */
+static int mod_detach(UNUSED void *instance)
+{
+#ifdef WITH_AUTH_WINBIND
+       rlm_mschap_t *inst = instance;
+
+       fr_connection_pool_free(inst->wb_pool);
+#endif
+
+       return 0;
+}
+
+/*
  *     add_reply() adds either MS-CHAP2-Success or MS-CHAP-Error
  *     attribute to reply packet
  */
@@ -603,7 +682,7 @@ void mschap_add_reply(REQUEST *request, unsigned char ident,
 {
        VALUE_PAIR *vp;
 
-       vp = pairmake_reply(name, NULL, T_OP_EQ);
+       vp = pair_make_reply(name, NULL, T_OP_EQ);
        if (!vp) {
                REDEBUG("Failed to create attribute %s: %s", name, fr_strerror());
                return;
@@ -634,13 +713,13 @@ static void mppe_add_reply(REQUEST *request, char const* name, uint8_t const * v
 {
        VALUE_PAIR *vp;
 
-       vp = pairmake_reply(name, NULL, T_OP_EQ);
+       vp = pair_make_reply(name, NULL, T_OP_EQ);
        if (!vp) {
               REDEBUG("mppe_add_reply failed to create attribute %s: %s", name, fr_strerror());
               return;
        }
 
-       pairmemcpy(vp, value, len);
+       fr_pair_value_memcpy(vp, value, len);
 }
 
 static int write_all(int fd, char const *buf, int len) {
@@ -668,9 +747,9 @@ static int CC_HINT(nonnull (1, 2, 4, 5)) do_mschap_cpw(rlm_mschap_t *inst,
 #endif
                                                       uint8_t *new_nt_password,
                                                       uint8_t *old_nt_hash,
-                                                      bool do_ntlm_auth)
+                                                      MSCHAP_AUTH_METHOD method)
 {
-       if (inst->ntlm_cpw && do_ntlm_auth) {
+       if (inst->ntlm_cpw && method != AUTH_INTERNAL) {
                /*
                 * we're going to run ntlm_auth in helper-mode
                 * we're expecting to use the ntlm-change-password-1 protocol
@@ -907,7 +986,7 @@ ntlm_auth_err:
                 *  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);
+               new_hash = pair_make_request("MS-CHAP-New-NT-Password", NULL, T_OP_EQ);
                new_hash->vp_length = NT_DIGEST_LENGTH;
                new_hash->vp_octets = q = talloc_array(new_hash, uint8_t, new_hash->vp_length);
                fr_md4_calc(q, p, passlen);
@@ -929,7 +1008,7 @@ ntlm_auth_err:
                 *
                 *  First pass: get the length of the converted string.
                 */
-               new_pass = pairmake_packet("MS-CHAP-New-Cleartext-Password", NULL, T_OP_EQ);
+               new_pass = pair_make_request("MS-CHAP-New-Cleartext-Password", NULL, T_OP_EQ);
                new_pass->vp_length = 0;
 
                i = 0;
@@ -998,7 +1077,7 @@ ntlm_auth_err:
                 *  fall through to the authentication code using the new hash,
                 *  not the old one.
                 */
-               pairmemcpy(nt_password, new_hash->vp_octets, new_hash->vp_length);
+               fr_pair_value_memcpy(nt_password, new_hash->vp_octets, new_hash->vp_length);
 
                /*
                 *  Rock on! password change succeeded.
@@ -1024,15 +1103,17 @@ ntlm_auth_err:
  */
 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[NT_DIGEST_LENGTH], bool do_ntlm_auth)
+                                                     uint8_t nthashhash[NT_DIGEST_LENGTH], MSCHAP_AUTH_METHOD method)
 {
        uint8_t calculated[24];
 
        memset(nthashhash, 0, NT_DIGEST_LENGTH);
-       /*
-        *      Do normal authentication.
-        */
-       if (!do_ntlm_auth) {
+
+       switch (method) {
+               /*
+                *      Do normal authentication.
+                */
+       case AUTH_INTERNAL:
                /*
                 *      No password: can't do authentication.
                 */
@@ -1055,7 +1136,12 @@ static int CC_HINT(nonnull (1, 2, 4, 5 ,6)) do_mschap(rlm_mschap_t *inst, REQUES
                    (password->da->attr == PW_NT_PASSWORD)) {
                        fr_md4_calc(nthashhash, password->vp_octets, MD4_DIGEST_LENGTH);
                }
-       } else {                /* run ntlm_auth */
+               break;
+
+               /*
+                *      Run ntlm_auth
+                */
+       case AUTH_NTLMAUTH_EXEC: {
                int     result;
                char    buffer[256];
                size_t  len;
@@ -1063,7 +1149,7 @@ static int CC_HINT(nonnull (1, 2, 4, 5 ,6)) do_mschap(rlm_mschap_t *inst, REQUES
                /*
                 *      Run the program, and expect that we get 16
                 */
-               result = radius_exec_program(buffer, sizeof(buffer), NULL, request, inst->ntlm_auth, NULL,
+               result = radius_exec_program(request, buffer, sizeof(buffer), NULL, request, inst->ntlm_auth, NULL,
                                             true, true, inst->ntlm_auth_timeout);
                if (result != 0) {
                        char *p;
@@ -1114,6 +1200,21 @@ static int CC_HINT(nonnull (1, 2, 4, 5 ,6)) do_mschap(rlm_mschap_t *inst, REQUES
                        REDEBUG("Invalid output from ntlm_auth: NT_KEY has non-hex values");
                        return -1;
                }
+               break;
+               }
+
+#ifdef WITH_AUTH_WINBIND
+               /*
+                *      Process auth via the wbclient library
+                */
+       case AUTH_WBCLIENT:
+               return do_auth_wbclient(inst, request, challenge, response, nthashhash);
+#endif
+
+               /* We should never reach this line */
+       default:
+               RERROR("Internal error: Unknown mschap auth method (%d)", method);
+               return -1;
        }
 
        return 0;
@@ -1167,7 +1268,7 @@ static void mppe_GetMasterKey(uint8_t const *nt_hashhash,uint8_t const *nt_respo
                              uint8_t *masterkey)
 {
        uint8_t digest[20];
-       fr_SHA1_CTX Context;
+       fr_sha1_ctx Context;
 
        fr_sha1_init(&Context);
        fr_sha1_update(&Context,nt_hashhash,NT_DIGEST_LENGTH);
@@ -1184,7 +1285,7 @@ static void mppe_GetAsymmetricStartKey(uint8_t *masterkey,uint8_t *sesskey,
 {
        uint8_t digest[20];
        const uint8_t *s;
-       fr_SHA1_CTX Context;
+       fr_sha1_ctx Context;
 
        memset(digest,0,20);
 
@@ -1248,19 +1349,19 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void * instance, REQUEST *requ
        rlm_mschap_t *inst = instance;
        VALUE_PAIR *challenge = NULL;
 
-       challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
+       challenge = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
        if (!challenge) {
                return RLM_MODULE_NOOP;
        }
 
-       if (!pairfind(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY) &&
-           !pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY) &&
-           !pairfind(request->packet->vps, PW_MSCHAP2_CPW, VENDORPEC_MICROSOFT, TAG_ANY)) {
+       if (!fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY) &&
+           !fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY) &&
+           !fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_CPW, VENDORPEC_MICROSOFT, TAG_ANY)) {
                RDEBUG2("Found MS-CHAP-Challenge, but no MS-CHAP response or change-password");
                return RLM_MODULE_NOOP;
        }
 
-       if (pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY)) {
+       if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY)) {
                RWDEBUG2("Auth-Type already set.  Not setting to MS-CHAP");
                return RLM_MODULE_NOOP;
        }
@@ -1272,13 +1373,86 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void * instance, REQUEST *requ
         *      will take care of turning cleartext passwords into
         *      NT/LM passwords.
         */
-       if (!pairmake_config("Auth-Type", inst->auth_type, T_OP_EQ)) {
+       if (!pair_make_config("Auth-Type", inst->auth_type, T_OP_EQ)) {
                return RLM_MODULE_FAIL;
        }
 
        return RLM_MODULE_OK;
 }
 
+static rlm_rcode_t mschap_error(rlm_mschap_t *inst, REQUEST *request, unsigned char ident,
+                               int mschap_result, int mschap_version, VALUE_PAIR *smb_ctrl)
+{
+       rlm_rcode_t     rcode = RLM_MODULE_OK;
+       int             error = 0;
+       int             retry = 0;
+       char const      *message = NULL;
+
+       int             i;
+       char            new_challenge[33], buffer[128];
+       char            *p;
+
+       if ((smb_ctrl && ((smb_ctrl->vp_integer & ACB_PW_EXPIRED) != 0)) || (mschap_result == -648)) {
+               REDEBUG("Password has expired.  User should retry authentication");
+               error = 648;
+               retry = inst->allow_retry ? 1 : 0;
+               message = "Password expired";
+               rcode = RLM_MODULE_REJECT;
+
+       } else if (mschap_result < 0) {
+               REDEBUG("MS-CHAP2-Response is incorrect");
+               error = 691;
+               retry = inst->allow_retry ? 1 : 0;
+               message = "Authentication failed";
+               rcode = RLM_MODULE_REJECT;
+       /*
+        *      Account is disabled.
+        *
+        *      They're found, but they don't exist, so we
+        *      return 'not found'.
+        */
+       } else if (smb_ctrl && (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) ||
+                               ((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0))) {
+               REDEBUG("SMB-Account-Ctrl says that the account is disabled, or is not a normal "
+                       "or workstation trust account");
+               error = 691;
+               retry = 1;
+               message = "Account disabled";
+               rcode = RLM_MODULE_NOTFOUND;
+       /*
+        *      User is locked out.
+        */
+       } else if (smb_ctrl && ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0)) {
+               REDEBUG("SMB-Account-Ctrl says that the account is locked out");
+               error = 647;
+               retry = 0;
+               message = "Account locked out";
+               rcode = RLM_MODULE_USERLOCK;
+       }
+
+       if (rcode == RLM_MODULE_OK) return RLM_MODULE_OK;
+
+       switch (mschap_version) {
+       case 1:
+               for (p = new_challenge, i = 0; i < 2; i++) p += snprintf(p, 9, "%08x", fr_rand());
+               snprintf(buffer, sizeof(buffer), "E=%i R=%i C=%s V=2",
+                        error, retry, new_challenge);
+               break;
+
+       case 2:
+               for (p = new_challenge, i = 0; i < 4; i++) p += snprintf(p, 9, "%08x", fr_rand());
+               snprintf(buffer, sizeof(buffer), "E=%i R=%i C=%s V=3 M=%s",
+                        error, retry, new_challenge, message);
+               break;
+
+       default:
+               rad_assert(0);
+       }
+       mschap_add_reply(request, ident, "MS-CHAP-Error", buffer, strlen(buffer));
+
+       return rcode;
+}
+
 /*
  *     mod_authenticate() - authenticate user based on given
  *     attributes and configuration.
@@ -1296,9 +1470,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void * instance, REQUEST *requ
  *     If MS-CHAP2 succeeds we MUST return
  *     PW_MSCHAP2_SUCCESS
  */
-static rlm_rcode_t CC_HINT(nonnull) 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)
+       rlm_mschap_t *inst = instance;
        VALUE_PAIR *challenge = NULL;
        VALUE_PAIR *response = NULL;
        VALUE_PAIR *cpw = NULL;
@@ -1307,35 +1481,35 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
        VALUE_PAIR *username;
        uint8_t nthashhash[NT_DIGEST_LENGTH];
        char msch2resp[42];
-       uint8_t *p;
        char const *username_string;
-       int chap = 0;
-       bool do_ntlm_auth;
+       int mschap_version = 0;
+       int mschap_result;
+       MSCHAP_AUTH_METHOD auth_method;
 
        /*
         *      If we have ntlm_auth configured, use it unless told
         *      otherwise
         */
-       do_ntlm_auth = (inst->ntlm_auth != NULL);
+       auth_method = inst->method;
 
        /*
         *      If we have an ntlm_auth configuration, then we may
         *      want to suppress it.
         */
-       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 > 0);
+       if (auth_method != AUTH_INTERNAL) {
+               VALUE_PAIR *vp = fr_pair_find_by_num(request->config, PW_MS_CHAP_USE_NTLM_AUTH, 0, TAG_ANY);
+               if (vp && vp->vp_integer == 0) auth_method = AUTH_INTERNAL;
        }
 
        /*
         *      Find the SMB-Account-Ctrl attribute, or the
         *      SMB-Account-Ctrl-Text attribute.
         */
-       smb_ctrl = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL, 0, TAG_ANY);
+       smb_ctrl = fr_pair_find_by_num(request->config, PW_SMB_ACCOUNT_CTRL, 0, TAG_ANY);
        if (!smb_ctrl) {
-               password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT, 0, TAG_ANY);
+               password = fr_pair_find_by_num(request->config, PW_SMB_ACCOUNT_CTRL_TEXT, 0, TAG_ANY);
                if (password) {
-                       smb_ctrl = pairmake_config("SMB-Account-CTRL", "0", T_OP_SET);
+                       smb_ctrl = pair_make_config("SMB-Account-CTRL", "0", T_OP_SET);
                        if (smb_ctrl) {
                                smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue);
                        }
@@ -1359,12 +1533,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
        /*
         *      Decide how to get the passwords.
         */
-       password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+       password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
 
        /*
         *      We need an NT-Password.
         */
-       nt_password = pairfind(request->config_items, PW_NT_PASSWORD, 0, TAG_ANY);
+       nt_password = fr_pair_find_by_num(request->config, PW_NT_PASSWORD, 0, TAG_ANY);
        if (nt_password) {
                VERIFY_VP(nt_password);
 
@@ -1393,9 +1567,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
         *      ... or a Cleartext-Password, which we now transform into an NT-Password
         */
        if (!nt_password) {
+               uint8_t *p;
+
                if (password) {
                        RDEBUG2("Found Cleartext-Password, hashing to create NT-Password");
-                       nt_password = pairmake_config("NT-Password", NULL, T_OP_EQ);
+                       nt_password = pair_make_config("NT-Password", NULL, T_OP_EQ);
                        if (!nt_password) {
                                RERROR("No memory");
                                return RLM_MODULE_FAIL;
@@ -1407,7 +1583,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                                RERROR("Failed generating NT-Password");
                                return RLM_MODULE_FAIL;
                        }
-               } else if (!do_ntlm_auth) {
+               } else if (auth_method == AUTH_INTERNAL) {
                        RWDEBUG2("No Cleartext-Password configured.  Cannot create NT-Password");
                }
        }
@@ -1415,7 +1591,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
        /*
         *      Or an LM-Password.
         */
-       lm_password = pairfind(request->config_items, PW_LM_PASSWORD, 0, TAG_ANY);
+       lm_password = fr_pair_find_by_num(request->config, PW_LM_PASSWORD, 0, TAG_ANY);
        if (lm_password) {
                VERIFY_VP(lm_password);
 
@@ -1445,10 +1621,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
        if (!lm_password) {
                if (password) {
                        RDEBUG2("Found Cleartext-Password, hashing to create LM-Password");
-                       lm_password = pairmake_config("LM-Password", NULL, T_OP_EQ);
+                       lm_password = pair_make_config("LM-Password", NULL, T_OP_EQ);
                        if (!lm_password) {
                                RERROR("No memory");
                        } else {
+                               uint8_t *p;
+
                                lm_password->vp_length = LM_DIGEST_LENGTH;
                                lm_password->vp_octets = p = talloc_array(lm_password, uint8_t, lm_password->vp_length);
                                smbdes_lmpwdhash(password->vp_strvalue, p);
@@ -1456,12 +1634,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                /*
                 *      Only complain if we don't have NT-Password
                 */
-               } else if (!do_ntlm_auth && !nt_password) {
+               } else if ((auth_method == AUTH_INTERNAL) && !nt_password) {
                        RWDEBUG2("No Cleartext-Password configured.  Cannot create LM-Password");
                }
        }
 
-       cpw = pairfind(request->packet->vps, PW_MSCHAP2_CPW, VENDORPEC_MICROSOFT, TAG_ANY);
+       cpw = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_CPW, VENDORPEC_MICROSOFT, TAG_ANY);
        if (cpw) {
                /*
                 * mschap2 password change request
@@ -1469,17 +1647,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                 * we then extract the response, add it into the request
                 * then jump into mschap2 auth with the chal/resp
                 */
-               uint8_t new_nt_encrypted[516], old_nt_encrypted[NT_DIGEST_LENGTH];
-               VALUE_PAIR *nt_enc=NULL;
-               int seq, new_nt_enc_len=0;
+               uint8_t         new_nt_encrypted[516], old_nt_encrypted[NT_DIGEST_LENGTH];
+               VALUE_PAIR      *nt_enc=NULL;
+               int             seq, new_nt_enc_len=0;
+               uint8_t         *p;
 
                RDEBUG("MS-CHAPv2 password change request received");
 
-               if (!nt_password) {
-                       REDEBUG("No valid NT-Password attribute found, can't change password");
-                       return RLM_MODULE_INVALID;
-               }
-
                if (cpw->vp_length != 68) {
                        REDEBUG("MS-CHAP2-CPW has the wrong format: length %zu != 68", cpw->vp_length);
                        return RLM_MODULE_INVALID;
@@ -1556,7 +1730,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
 
                /* 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) {
+               if (do_mschap_cpw(inst, request, nt_password, new_nt_encrypted, old_nt_encrypted, auth_method) < 0) {
                        char buffer[128];
 
                        REDEBUG("Password change failed");
@@ -1583,8 +1757,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                 *  change, add them into the request and then continue with
                 *  the authentication
                 */
-
-               response = radius_paircreate(request->packet, &request->packet->vps,
+               response = radius_pair_create(request->packet, &request->packet->vps,
                                             PW_MSCHAP2_RESPONSE,
                                             VENDORPEC_MICROSOFT);
                response->vp_length = 50;
@@ -1597,7 +1770,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                memcpy(p + 2, cpw->vp_octets + 18, 48);
        }
 
-       challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
+       challenge = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
        if (!challenge) {
                REDEBUG("You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
                return RLM_MODULE_REJECT;
@@ -1606,13 +1779,15 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
        /*
         *      We also require an MS-CHAP-Response.
         */
-       response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
+       response = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
 
        /*
         *      MS-CHAP-Response, means MS-CHAPv1
         */
        if (response) {
-               int offset;
+               int             offset;
+               rlm_rcode_t     rcode;
+               mschap_version = 1;
 
                /*
                 *      MS-CHAPv1 challenges are 8 octets.
@@ -1647,18 +1822,21 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                /*
                 *      Do the MS-CHAP authentication.
                 */
-               if (do_mschap(inst, request, password, challenge->vp_octets, response->vp_octets + offset, nthashhash,
-                             do_ntlm_auth) < 0) {
-                       REDEBUG("MS-CHAP-Response is incorrect");
-                       goto do_error;
-               }
-
-               chap = 1;
+               mschap_result = do_mschap(inst, request, password, challenge->vp_octets,
+                                         response->vp_octets + offset, nthashhash, auth_method);
+               /*
+                *      Check for errors, and add MSCHAP-Error if necessary.
+                */
+               rcode = mschap_error(inst, request, *response->vp_octets,
+                                    mschap_result, mschap_version, smb_ctrl);
+               if (rcode != RLM_MODULE_OK) return rcode;
+       } else if ((response = fr_pair_find_by_num(request->packet->vps, PW_MSCHAP2_RESPONSE,
+                                                  VENDORPEC_MICROSOFT, TAG_ANY)) != NULL) {
+               uint8_t         mschapv1_challenge[16];
+               VALUE_PAIR      *name_attr, *response_name;
+               rlm_rcode_t     rcode;
 
-       } else if ((response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY)) != NULL) {
-               int mschap_result;
-               uint8_t mschapv1_challenge[16];
-               VALUE_PAIR *name_attr, *response_name;
+               mschap_version = 2;
 
                /*
                 *      MS-CHAPv2 challenges are 16 octets.
@@ -1679,7 +1857,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                /*
                 *      We also require a User-Name
                 */
-               username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!username) {
                        REDEBUG("We require a User-Name for MS-CHAPv2");
                        return RLM_MODULE_INVALID;
@@ -1694,7 +1872,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                 *      We prefer this to the User-Name in the
                 *      packet.
                 */
-               response_name = pairfind(request->packet->vps, PW_MS_CHAP_USER_NAME, 0, TAG_ANY);
+               response_name = fr_pair_find_by_num(request->packet->vps, PW_MS_CHAP_USER_NAME, 0, TAG_ANY);
                if (response_name) {
                        name_attr = response_name;
                } else {
@@ -1721,7 +1899,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                                username->vp_strvalue, response_name->vp_strvalue);
                }
 
-#ifdef WITH_OPEN_DIRECTORY
+#ifdef __APPLE__
                /*
                 *  No "known good" NT-Password attribute.  Try to do
                 *  OpenDirectory authentication.
@@ -1752,53 +1930,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                                      mschapv1_challenge);      /* resulting challenge */
 
                RDEBUG2("Client is using MS-CHAPv2");
-
                mschap_result = do_mschap(inst, request, nt_password, mschapv1_challenge,
-                                         response->vp_octets + 26, nthashhash, do_ntlm_auth);
-               if (mschap_result == -648)
-                       goto password_expired;
-
-               if (mschap_result < 0) {
-                       int i;
-                       char buffer[128];
-
-                       REDEBUG("MS-CHAP2-Response is incorrect");
-
-               do_error:
-                       snprintf(buffer, sizeof(buffer), "E=691 R=%d", inst->allow_retry);
-
-                       if (inst->retry_msg) {
-                               snprintf(buffer + 9, sizeof(buffer) - 9, " C=");
-                               for (i = 0; i < 16; i++) {
-                                       snprintf(buffer + 12 + (i * 2),
-                                                sizeof(buffer) - 12 - (i * 2), "%02x",
-                                                fr_rand() & 0xff);
-                               }
-                               snprintf(buffer + 45, sizeof(buffer) - 45, " V=3 M=%s", inst->retry_msg);
-                       }
-                       mschap_add_reply(request, *response->vp_octets, "MS-CHAP-Error", buffer, strlen(buffer));
-                       return RLM_MODULE_REJECT;
-               }
-
-               /*
-                *      If the password is correct and it has expired
-                *      we can permit password changes (only in MS-CHAPv2)
-                */
-               if (smb_ctrl && smb_ctrl->vp_integer & ACB_PW_EXPIRED) {
-
-                       char newchal[33], buffer[128];
-                       int i;
-               password_expired:
-
-                       for (i = 0; i < 16; i++) {
-                               snprintf(newchal + (i * 2), 3, "%02x", fr_rand() & 0xff);
-                       }
-
-                       snprintf(buffer, sizeof(buffer), "E=648 R=0 C=%s V=3 M=Password Expired", newchal);
-
-                       mschap_add_reply(request, *response->vp_octets, "MS-CHAP-Error", buffer, strlen(buffer));
-                       return RLM_MODULE_REJECT;
-               }
+                                         response->vp_octets + 26, nthashhash, auth_method);
+               rcode = mschap_error(inst, request, *response->vp_octets,
+                                    mschap_result, mschap_version, smb_ctrl);
+               if (rcode != RLM_MODULE_OK) return rcode;
 
                mschap_auth_response(username_string,           /* without the domain */
                                     nthashhash,                /* nt-hash-hash */
@@ -1807,49 +1943,18 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                                     challenge->vp_octets,      /* our challenge */
                                     msch2resp);                /* calculated MPPE key */
                mschap_add_reply(request, *response->vp_octets, "MS-CHAP2-Success", msch2resp, 42);
-               chap = 2;
 
        } else {                /* Neither CHAPv1 or CHAPv2 response: die */
                REDEBUG("You set 'Auth-Type = MS-CHAP' for a request that does not contain any MS-CHAP attributes!");
                return RLM_MODULE_INVALID;
        }
 
-       /*
-        *      We have a CHAP response, but the account may be
-        *      disabled.  Reject the user with the same error code
-        *      we use when their password is invalid.
-        */
-       if (smb_ctrl) {
-               /*
-                *      Account is disabled.
-                *
-                *      They're found, but they don't exist, so we
-                *      return 'not found'.
-                */
-               if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) ||
-                   ((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0)) {
-                       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;
-               }
-
-               /*
-                *      User is locked out.
-                */
-               if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) {
-                       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;
-               }
-       }
-
        /* now create MPPE attributes */
        if (inst->use_mppe) {
                uint8_t mppe_sendkey[34];
                uint8_t mppe_recvkey[34];
 
-               if (chap == 1){
+               if (mschap_version == 1) {
                        RDEBUG2("adding MS-CHAPv1 MPPE keys");
                        memset(mppe_sendkey, 0, 32);
                        if (lm_password) {
@@ -1862,15 +1967,16 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                         *      practice it doesn't work.
                         *      Instead, we should send nthashhash
                         *
-                        *      This is an error on RFC 2548.
+                        *      This is an error in RFC 2548.
                         */
                        /*
                         *      do_mschap cares to zero nthashhash if NT hash
                         *      is not available.
                         */
                        memcpy(mppe_sendkey + 8, nthashhash, NT_DIGEST_LENGTH);
-                       mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32);
-               } else if (chap == 2) {
+                       mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 24);
+
+               } else if (mschap_version == 2) {
                        RDEBUG2("Adding MS-CHAPv2 MPPE keys");
                        mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey);
 
@@ -1878,9 +1984,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
                        mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16);
 
                }
-               pairmake_reply("MS-MPPE-Encryption-Policy",
+               pair_make_reply("MS-MPPE-Encryption-Policy",
                               (inst->require_encryption) ? "0x00000002":"0x00000001", T_OP_EQ);
-               pairmake_reply("MS-MPPE-Encryption-Types",
+               pair_make_reply("MS-MPPE-Encryption-Types",
                               (inst->require_strong) ? "0x00000004":"0x00000006", T_OP_EQ);
        } /* else we weren't asked to use MPPE */
 
@@ -1890,21 +1996,16 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *r
 
 extern module_t rlm_mschap;
 module_t rlm_mschap = {
-       RLM_MODULE_INIT,
-       "MS-CHAP",
-       RLM_TYPE_THREAD_SAFE | RLM_TYPE_HUP_SAFE,       /* type */
-       sizeof(rlm_mschap_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authenticate */
-               mod_authorize,          /* authorize */
-               NULL,                   /* pre-accounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "mschap",
+       .type           = 0,
+       .inst_size      = sizeof(rlm_mschap_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
diff --git a/src/modules/rlm_mschap/rlm_mschap.h b/src/modules/rlm_mschap/rlm_mschap.h
new file mode 100644 (file)
index 0000000..1ce1ad4
--- /dev/null
@@ -0,0 +1,48 @@
+/* Copyright 2006-2015 The FreeRADIUS server project */
+
+#ifndef _RLM_MSCHAP_H
+#define _RLM_MSCHAP_H
+
+RCSIDH(rlm_mschap_h, "$Id$")
+
+#include "config.h"
+
+#ifdef WITH_AUTH_WINBIND
+#  include <wbclient.h>
+#endif
+
+/* Method of authentication we are going to use */
+typedef enum {
+       AUTH_INTERNAL           = 0,
+       AUTH_NTLMAUTH_EXEC      = 1
+#ifdef WITH_AUTH_WINBIND
+       ,AUTH_WBCLIENT          = 2
+#endif
+} MSCHAP_AUTH_METHOD;
+
+typedef struct rlm_mschap_t {
+       bool                    use_mppe;
+       bool                    require_encryption;
+       bool                    require_strong;
+       bool                    with_ntdomain_hack;     /* this should be in another module */
+       char const              *xlat_name;
+       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 const              *retry_msg;
+       MSCHAP_AUTH_METHOD      method;
+       vp_tmpl_t               *wb_username;
+       vp_tmpl_t               *wb_domain;
+       fr_connection_pool_t    *wb_pool;
+#ifdef __APPLE__
+       bool                    open_directory;
+#endif
+} rlm_mschap_t;
+
+#endif
+
index e8c3116..580c62b 100644 (file)
@@ -361,15 +361,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
        uuid_clear(guid_sacl);
 
        if (rad_getgid(request, &gid, kRadiusSACLName) < 0) {
+               RDEBUG("The SACL group \"%s\" does not exist on this system.", kRadiusSACLName);
+       } else {
                err = mbr_gid_to_uuid(gid, guid_sacl);
                if (err != 0) {
                        ERROR("rlm_opendirectory: The group \"%s\" does not have a GUID.", kRadiusSACLName);
                        return RLM_MODULE_FAIL;
                }
        }
-       else {
-               RDEBUG("The SACL group \"%s\" does not exist on this system.", kRadiusSACLName);
-       }
 
        /* resolve client access list */
        uuid_clear(guid_nasgroup);
@@ -413,8 +412,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
 
        if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) {
                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);
+               if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY) == NULL) {
+                       pair_make_config("Auth-Type", kAuthType, T_OP_EQ);
                        RDEBUG("Setting Auth-Type = %s", kAuthType);
                }
                return RLM_MODULE_OK;
@@ -462,8 +461,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
                }
        }
 
-       if (pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY) == NULL) {
-               pairmake_config("Auth-Type", kAuthType, T_OP_EQ);
+       if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY) == NULL) {
+               pair_make_config("Auth-Type", kAuthType, T_OP_EQ);
                RDEBUG("Setting Auth-Type = %s", kAuthType);
        }
 
@@ -474,21 +473,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
 /* globally exported name */
 extern module_t rlm_opendirectory;
 module_t rlm_opendirectory = {
-       RLM_MODULE_INIT,
-       "opendirectory",
-       RLM_TYPE_THREAD_SAFE,   /* type */
-       0,
-       NULL,                   /* CONF_PARSER */
-       NULL,                   /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate, /* authentication */
-               mod_authorize,  /* authorization */
-               NULL,           /* preaccounting */
-               NULL,           /* accounting */
-               NULL,           /* checksimul */
-               NULL,           /* pre-proxy */
-               NULL,           /* post-proxy */
-               NULL            /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "opendirectory",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
index a7e6018..399689a 100644 (file)
@@ -46,22 +46,22 @@ void otp_mppe(REQUEST *request, otp_pwe_t pwe, rlm_otp_t const *opt, char const
 {
        VALUE_PAIR *cvp, *rvp;
 
-       cvp = pair_find_by_da(request->packet->vps, pwattr[pwe - 1], TAG_ANY);
-       rvp = pair_find_by_da(request->packet->vps, pwattr[pwe], TAG_ANY);
+       cvp = fr_pair_find_by_da(request->packet->vps, pwattr[pwe - 1], TAG_ANY);
+       rvp = fr_pair_find_by_da(request->packet->vps, pwattr[pwe], TAG_ANY);
        if (!cvp || !rvp) {
                return;
        }
 
        switch (pwe) {
-       case PW_NONE:
+       case PWE_NONE:
        case PWE_PAP:
        case PWE_CHAP:
                return;
 
        case PWE_MSCHAP:
                /* First, set some related attributes. */
-               pairmake_reply("MS-MPPE-Encryption-Policy", otp_mppe_policy[opt->mschap_mppe_policy], T_OP_EQ);
-               pairmake_reply("MS-MPPE-Encryption-Types", otp_mppe_types[opt->mschap_mppe_types], T_OP_EQ);
+               pair_make_reply("MS-MPPE-Encryption-Policy", otp_mppe_policy[opt->mschap_mppe_policy], T_OP_EQ);
+               pair_make_reply("MS-MPPE-Encryption-Types", otp_mppe_types[opt->mschap_mppe_types], T_OP_EQ);
 
                /* If no MPPE, we're done. */
                if (!opt->mschap_mppe_policy) {
@@ -109,7 +109,7 @@ void otp_mppe(REQUEST *request, otp_pwe_t pwe, rlm_otp_t const *opt, char const
                        /* second md4 */
                        (void) MD4(password_md, MD4_DIGEST_LENGTH, &mppe_keys[8]);
 
-                       /* Whew. Now stringify it for pairmake(). */
+                       /* Whew. Now stringify it for fr_pair_make(). */
                        mppe_keys_string[0] = '0';
                        mppe_keys_string[1] = 'x';
 
@@ -117,7 +117,7 @@ void otp_mppe(REQUEST *request, otp_pwe_t pwe, rlm_otp_t const *opt, char const
                                (void) sprintf(&mppe_keys_string[i*2+2], "%02X", mppe_keys[i]);
                        }
 
-                       pairmake_reply("MS-CHAP-MPPE-Keys", mppe_keys_string, T_OP_EQ);
+                       pair_make_reply("MS-CHAP-MPPE-Keys", mppe_keys_string, T_OP_EQ);
                } /* (doing mppe) */
        break; /* PWE_MSCHAP */
 
@@ -150,7 +150,7 @@ void otp_mppe(REQUEST *request, otp_pwe_t pwe, rlm_otp_t const *opt, char const
                        /*
                         * ugh. The ASCII authenticator (auth_md_string) is sent
                         * along with a single (useless) binary byte (the ID).
-                        * So we must "stringify" it again (for pairmake()) since the
+                        * So we must "stringify" it again (for fr_pair_make()) since the
                         * binary byte requires the attribute to be of type "octets".
                         */
                        /* 0x(ID)(ASCII("S="ASCII(auth_md))) */
@@ -228,14 +228,14 @@ void otp_mppe(REQUEST *request, otp_pwe_t pwe, rlm_otp_t const *opt, char const
                                (void) sprintf(&auth_octet_string[i * 2 +4], "%02X", auth_md_string[i]);
                        }
 
-                       pairmake_reply("MS-CHAP2-Success", auth_octet_string, T_OP_EQ);
+                       pair_make_reply("MS-CHAP2-Success", auth_octet_string, T_OP_EQ);
                } /* Generate mutual auth info. */
 
                /*
                 * Now, set some MPPE related attributes.
                 */
-               pairmake_reply("MS-MPPE-Encryption-Policy", otp_mppe_policy[opt->mschapv2_mppe_policy], T_OP_EQ);
-               pairmake_reply("MS-MPPE-Encryption-Types", otp_mppe_types[opt->mschapv2_mppe_types], T_OP_EQ);
+               pair_make_reply("MS-MPPE-Encryption-Policy", otp_mppe_policy[opt->mschapv2_mppe_policy], T_OP_EQ);
+               pair_make_reply("MS-MPPE-Encryption-Types", otp_mppe_types[opt->mschapv2_mppe_types], T_OP_EQ);
 
                /* If no MPPE, we're done. */
                if (!opt->mschapv2_mppe_policy) {
@@ -353,7 +353,7 @@ void otp_mppe(REQUEST *request, otp_pwe_t pwe, rlm_otp_t const *opt, char const
                                (void) sprintf(&mppe_key_string[i*2+2], "%02X", MasterSendKey[i]);
                        }
 
-                       pairmake_reply("MS-MPPE-Send-Key", mppe_key_string, T_OP_EQ);
+                       pair_make_reply("MS-MPPE-Send-Key", mppe_key_string, T_OP_EQ);
 
                        /*
                         * Generate the MS-MPPE-Recv-Key attribute.
@@ -363,7 +363,7 @@ void otp_mppe(REQUEST *request, otp_pwe_t pwe, rlm_otp_t const *opt, char const
                        for (i = 0; i < sizeof(MasterReceiveKey); ++i) {
                                (void) sprintf(&mppe_key_string[i*2+2], "%02X", MasterReceiveKey[i]);
                        }
-                       pairmake_reply("MS-MPPE-Recv-Key", mppe_key_string, T_OP_EQ);
+                       pair_make_reply("MS-MPPE-Recv-Key", mppe_key_string, T_OP_EQ);
                } /* (doing mppe) */
 
                break; /* PWE_MSCHAP2 */
index 8b29bca..bf17d9c 100644 (file)
@@ -99,10 +99,10 @@ int otp_pw_valid(REQUEST *request, int pwe, char const *challenge,
         *      otp_pwe_present() (done by caller) guarantees that both of
         *      these exist
         */
-       cvp = pairfind(request->packet->vps, pwattr[pwe - 1]->attr,
+       cvp = fr_pair_find_by_num(request->packet->vps, pwattr[pwe - 1]->attr,
                       pwattr[pwe - 1]->vendor, TAG_ANY);
 
-       rvp = pairfind(request->packet->vps, pwattr[pwe]->attr,
+       rvp = fr_pair_find_by_num(request->packet->vps, pwattr[pwe]->attr,
                       pwattr[pwe]->vendor, TAG_ANY);
 
        /* this is just to quiet Coverity */
index a5ec052..56a4dbc 100644 (file)
@@ -126,9 +126,9 @@ otp_pwe_t otp_pwe_present(REQUEST const *request)
                        continue;
                }
 
-               if (pairfind(request->packet->vps, pwattr[i]->attr,
+               if (fr_pair_find_by_num(request->packet->vps, pwattr[i]->attr,
                             pwattr[i]->vendor, TAG_ANY) &&
-                   pairfind(request->packet->vps, pwattr[i + 1]->attr,
+                   fr_pair_find_by_num(request->packet->vps, pwattr[i + 1]->attr,
                             pwattr[i + 1]->vendor, TAG_ANY)) {
                        DEBUG("rlm_otp: %s: password attributes %s, %s",
                              __func__, pwattr[i]->name, pwattr[i + 1]->name);
index e842a53..a703939 100644 (file)
@@ -35,12 +35,12 @@ USES_APPLE_DEPRECATED_API   /* OpenSSL API has been deprecated by Apple */
 #include <openssl/hmac.h>
 
 /*
- * Generate the State attribute, suitable for passing to pairmake().
+ * Generate the State attribute, suitable for passing to fr_pair_make().
  * 'challenge' must be a null terminated string, and be sized at least
  * as large as indicated in the function definition.
  *
  * Returns 0 on success, non-zero otherwise.  For successful returns,
- * 'rad_state' (suitable for passing to pairmake()) and 'raw_state',
+ * 'rad_state' (suitable for passing to fr_pair_make()) and 'raw_state',
  * if non-NULL, will be filled in.
  *
  * In the simplest implementation, we would just use the challenge as state.
index 8e2ff30..662a202 100644 (file)
@@ -48,8 +48,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -135,7 +134,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                VALUE_PAIR *vp;
 
                auth_type_found = 0;
-               vp = pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
                if (vp) {
                        auth_type_found = 1;
                        if (strcmp(vp->vp_strvalue, inst->name)) {
@@ -145,7 +144,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        }
 
        /* The State attribute will be present if this is a response. */
-       if (pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY) != NULL) {
+       if (fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY) != NULL) {
                DEBUG("rlm_otp: autz: Found response to Access-Challenge");
 
                return RLM_MODULE_OK;
@@ -178,7 +177,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        if (inst->allow_sync && !inst->allow_async) {
                /* This is the token sync response. */
                if (!auth_type_found) {
-                       pairmake_config("Auth-Type", inst->name, T_OP_EQ);
+                       pair_make_config("Auth-Type", inst->name, T_OP_EQ);
                }
 
                return RLM_MODULE_OK;
@@ -214,13 +213,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                len = otp_gen_state(gen_state, challenge, inst->challenge_len,
                                    0, now, inst->hmac_key);
 
-               vp = paircreate(request->reply, PW_STATE, 0);
+               vp = fr_pair_afrom_num(request->reply, PW_STATE, 0);
                if (!vp) {
                        return RLM_MODULE_FAIL;
                }
 
-               pairmemcpy(vp, (uint8_t const *) gen_state, len);
-               pairadd(&request->reply->vps, vp);
+               fr_pair_value_memcpy(vp, (uint8_t const *) gen_state, len);
+               fr_pair_add(&request->reply->vps, vp);
        }
 
        /*
@@ -236,15 +235,15 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                 *      First add the internal OTP challenge attribute to
                 *      the reply list.
                 */
-               vp = paircreate(request->reply, PW_OTP_CHALLENGE, 0);
+               vp = fr_pair_afrom_num(request->reply, PW_OTP_CHALLENGE, 0);
                if (!vp) {
                        return RLM_MODULE_FAIL;
                }
 
-               pairstrcpy(vp, challenge);
+               fr_pair_value_strcpy(vp, challenge);
                vp->op = T_OP_SET;
 
-               pairadd(&request->reply->vps, vp);
+               fr_pair_add(&request->reply->vps, vp);
 
                /*
                 *      Then add the message to the user to they known
@@ -256,7 +255,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                        return RLM_MODULE_FAIL;
                }
 
-               vp = paircreate(request->reply, PW_REPLY_MESSAGE, 0);
+               vp = fr_pair_afrom_num(request->reply, PW_REPLY_MESSAGE, 0);
                if (!vp) {
                        talloc_free(expanded);
                        return RLM_MODULE_FAIL;
@@ -268,7 +267,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                vp->op = T_OP_SET;
                vp->type = VT_DATA;
 
-               pairadd(&request->reply->vps, vp);
+               fr_pair_add(&request->reply->vps, vp);
        }
 
        /*
@@ -280,7 +279,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        DEBUG("rlm_otp: Sending Access-Challenge");
 
        if (!auth_type_found) {
-               pairmake_config("Auth-Type", inst->name, T_OP_EQ);
+               pair_make_config("Auth-Type", inst->name, T_OP_EQ);
        }
 
        return RLM_MODULE_HANDLED;
@@ -325,7 +324,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
        /*
         *      Retrieve the challenge (from State attribute).
         */
-       vp = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
        if (vp) {
                char    gen_state[OTP_MAX_RADSTATE_LEN]; //!< State as hexits
                uint8_t bin_state[OTP_MAX_RADSTATE_LEN];
@@ -419,21 +418,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
  */
 extern module_t rlm_otp;
 module_t rlm_otp = {
-       RLM_MODULE_INIT,
-       "otp",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_otp_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "otp",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_otp_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
index 3a46f42..2045906 100644 (file)
@@ -19,7 +19,7 @@ if test x$with_[]modname != xno; then
        )
 
 dnl #
-dnl #  Yes, these DO have to be on seperate lines,
+dnl #  Yes, these DO have to be on separate lines,
 dnl #  otherwise autoheader won't pick them up.
 dnl #
        AC_CHECK_HEADERS( \
index 120e6bd..70d8332 100644 (file)
@@ -53,7 +53,7 @@ typedef struct rlm_pam_t {
 
 static const CONF_PARSER module_config[] = {
        { "pam_auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_pam_t, pam_auth_name), "radiusd" },
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 typedef struct rlm_pam_data_t {
@@ -221,7 +221,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
         *      Let the 'users' file over-ride the PAM auth name string,
         *      for backwards compatibility.
         */
-       pair = pairfind(request->config_items, PW_PAM_AUTH, 0, TAG_ANY);
+       pair = fr_pair_find_by_num(request->config, PW_PAM_AUTH, 0, TAG_ANY);
        if (pair) pam_auth_string = pair->vp_strvalue;
 
        ret = do_pam(request, request->username->vp_strvalue, request->password->vp_strvalue, pam_auth_string);
@@ -232,22 +232,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
 
 extern module_t rlm_pam;
 module_t rlm_pam = {
-       RLM_MODULE_INIT,
-       "pam",
-       RLM_TYPE_THREAD_UNSAFE, /* The PAM libraries are not thread-safe */
-       sizeof(rlm_pam_t),
-       module_config,
-       NULL,                           /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authenticate */
-               NULL,                   /* authorize */
-               NULL,                   /* pre-accounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "pam",
+       .type           = RLM_TYPE_THREAD_UNSAFE,       /* The PAM libraries are not thread-safe */
+       .inst_size      = sizeof(rlm_pam_t),
+       .config         = module_config,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate
        },
 };
 
index 7533d1c..0e678ac 100644 (file)
@@ -64,7 +64,7 @@ typedef struct rlm_pap_t {
  */
 static const CONF_PARSER module_config[] = {
        { "normalise", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_pap_t, normify), "yes" },
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -98,6 +98,7 @@ static const FR_NAME_NUMBER header_names[] = {
 #endif
        { "{sha}",              PW_SHA_PASSWORD },
        { "{ssha}",             PW_SSHA_PASSWORD },
+       { "{md4}",              PW_NT_PASSWORD },
        { "{nt}",               PW_NT_PASSWORD },
        { "{nthash}",           PW_NT_PASSWORD },
        { "{x-nthash}",         PW_NT_PASSWORD },
@@ -162,7 +163,7 @@ static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_len)
                if (decoded == (vp->vp_length >> 1)) {
                        RDEBUG2("Normalizing %s from hex encoding, %zu bytes -> %zu bytes",
                                vp->da->name, vp->vp_length, decoded);
-                       pairmemcpy(vp, buffer, decoded);
+                       fr_pair_value_memcpy(vp, buffer, decoded);
                        return;
                }
        }
@@ -178,7 +179,7 @@ static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_len)
                if (decoded >= (ssize_t) min_len) {
                        RDEBUG2("Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
                                vp->da->name, vp->vp_length, decoded);
-                       pairmemcpy(vp, buffer, decoded);
+                       fr_pair_value_memcpy(vp, buffer, decoded);
                        return;
                }
        }
@@ -259,12 +260,12 @@ redo:
                 *      memcpy.  BUT it might be a string (or used as one), so
                 *      we ensure that there's a trailing zero, too.
                 */
-               new = paircreate(request, attr, 0);
+               new = fr_pair_afrom_num(request, attr, 0);
                if (new->da->type == PW_TYPE_OCTETS) {
-                       pairmemcpy(new, (uint8_t const *) q + 1, (len - hlen) + 1);
+                       fr_pair_value_memcpy(new, (uint8_t const *) q + 1, (len - hlen) + 1);
                        new->vp_length = (len - hlen);  /* lie about the length */
                } else {
-                       pairstrcpy(new, q + 1);
+                       fr_pair_value_strcpy(new, q + 1);
                }
 
                if (RDEBUG_ENABLED3) {
@@ -297,7 +298,7 @@ redo:
                 *      must be \0 terminated.
                 */
                digest[decoded] = '\0';
-               pairmemcpy(vp, digest, decoded + 1);
+               fr_pair_value_memcpy(vp, digest, decoded + 1);
                vp->vp_length = decoded;                /* lie about the length */
 
                goto redo;
@@ -311,8 +312,8 @@ redo:
        }
 
 unknown_header:
-       new = paircreate(request, PW_CLEARTEXT_PASSWORD, 0);
-       pairstrcpy(new, vp->vp_strvalue);
+       new = fr_pair_afrom_num(request, PW_CLEARTEXT_PASSWORD, 0);
+       fr_pair_value_strcpy(new, vp->vp_strvalue);
 
        return new;
 }
@@ -331,7 +332,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        VALUE_PAIR *vp;
        vp_cursor_t cursor;
 
-       for (vp = fr_cursor_init(&cursor, &request->config_items);
+       for (vp = fr_cursor_init(&cursor, &request->config);
             vp;
             vp = fr_cursor_next(&cursor)) {
                VERIFY_VP(vp);
@@ -353,7 +354,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                        /*
                         *      Password already exists: use that instead of this one.
                         */
-                       if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
+                       if (fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
                                RWDEBUG("Config already contains a \"known good\" password "
                                        "(&control:Cleartext-Password).  Ignoring &config:Password-With-Header");
                                break;
@@ -474,15 +475,15 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                 *      Likely going to be proxied.  Avoid printing
                 *      warning message.
                 */
-               if (pairfind(request->config_items, PW_REALM, 0, TAG_ANY) ||
-                   (pairfind(request->config_items, PW_PROXY_TO_REALM, 0, TAG_ANY))) {
+               if (fr_pair_find_by_num(request->config, PW_REALM, 0, TAG_ANY) ||
+                   (fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY))) {
                        return RLM_MODULE_NOOP;
                }
 
                /*
                 *      The TLS types don't need passwords.
                 */
-               vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY);
                if (vp &&
                    ((vp->vp_integer == 13) || /* EAP-TLS */
                     (vp->vp_integer == 21) || /* EAP-TTLS */
@@ -513,7 +514,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        }
 
        if (inst->auth_type) {
-               vp = radius_paircreate(request, &request->config_items,
+               vp = radius_pair_create(request, &request->config,
                                       PW_AUTH_TYPE, 0);
                vp->vp_integer = inst->auth_type;
        }
@@ -528,9 +529,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
 static rlm_rcode_t CC_HINT(nonnull) pap_auth_clear(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
        if (RDEBUG_ENABLED3) {
-               RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\"", vp->vp_strvalue);
+               RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\" (%zd)", vp->vp_strvalue, vp->vp_length);
        } else {
-               RDEBUG3("Comparing with \"known good\" Cleartext-Password");
+               RDEBUG("Comparing with \"known good\" Cleartext-Password");
        }
 
        if ((vp->vp_length != request->password->vp_length) ||
@@ -622,7 +623,7 @@ static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(rlm_pap_t *inst, REQUEST *requ
 
 static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
-       fr_SHA1_CTX sha1_context;
+       fr_sha1_ctx sha1_context;
        uint8_t digest[128];
 
        RDEBUG("Comparing with \"known-good\" SHA-Password");
@@ -650,7 +651,7 @@ static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha(rlm_pap_t *inst, REQUEST *reque
 
 static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
-       fr_SHA1_CTX sha1_context;
+       fr_sha1_ctx sha1_context;
        uint8_t digest[128];
 
        RDEBUG("Comparing with \"known-good\" SSHA-Password");
@@ -969,7 +970,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
        }
 
        if (RDEBUG_ENABLED3) {
-               RDEBUG3("Login attempt with password \"%s\"", request->password->vp_strvalue);
+               RDEBUG3("Login attempt with password \"%s\" (%zd)", request->password->vp_strvalue, request->password->vp_length);
        } else {
                RDEBUG("Login attempt with password");
        }
@@ -979,7 +980,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
         *      config items, to find out which authentication
         *      function to call.
         */
-       for (vp = fr_cursor_init(&cursor, &request->config_items);
+       for (vp = fr_cursor_init(&cursor, &request->config);
             vp;
             vp = fr_cursor_next(&cursor)) {
                if (!vp->da->vendor) switch (vp->da->attr) {
@@ -1075,21 +1076,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
  */
 extern module_t rlm_pap;
 module_t rlm_pap = {
-       RLM_MODULE_INIT,
-       "PAP",
-       RLM_TYPE_HUP_SAFE,      /* type */
-       sizeof(rlm_pap_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "pap",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_pap_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
index 51e91c0..e9957d2 100644 (file)
@@ -370,7 +370,7 @@ int main(void){
 }
 
 #else  /* TEST */
-struct passwd_instance {
+typedef struct rlm_passwd_t {
        struct hashtable        *ht;
        struct mypasswd         *pwdfmt;
        char const              *filename;
@@ -384,26 +384,25 @@ struct passwd_instance {
        uint32_t                listable;
        DICT_ATTR const         *keyattr;
        bool                    ignore_empty;
-};
+} rlm_passwd_t;
 
 static const CONF_PARSER module_config[] = {
-       { "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" },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, rlm_passwd_t, filename), NULL },
+       { "format", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_passwd_t, format), NULL },
+       { "delimiter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_passwd_t, delimiter), ":" },
 
-       { "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" },
+       { "ignorenislike", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_passwd_t, ignore_nislike), NULL },
+       { "ignore_nislike", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_passwd_t, ignore_nislike), "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" },
+       { "ignoreempty", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_passwd_t, ignore_empty), NULL },
+       { "ignore_empty",  FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_passwd_t, ignore_empty), "yes" },
 
-       { "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" },
+       { "allowmultiplekeys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_passwd_t, allow_multiple), NULL },
+       { "allow_multiple_keys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_passwd_t, allow_multiple), "no" },
 
-       { NULL, -1, 0, NULL, NULL }
+       { "hashsize", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_passwd_t, hash_size), NULL },
+       { "hash_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_passwd_t, hash_size), "100" },
+       CONF_PARSER_TERMINATOR
 };
 
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
@@ -414,7 +413,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        size_t len;
        int i;
        DICT_ATTR const * da;
-       struct passwd_instance *inst = instance;
+       rlm_passwd_t *inst = instance;
 
        rad_assert(inst->filename && *inst->filename);
        rad_assert(inst->format && *inst->format);
@@ -507,7 +506,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 }
 
 static int mod_detach (void *instance) {
-#define inst ((struct passwd_instance *)instance)
+#define inst ((rlm_passwd_t *)instance)
        if(inst->ht) {
                release_ht(inst->ht);
                inst->ht = NULL;
@@ -517,7 +516,7 @@ static int mod_detach (void *instance) {
 #undef inst
 }
 
-static void addresult (TALLOC_CTX *ctx, struct passwd_instance *inst, REQUEST *request,
+static void addresult (TALLOC_CTX *ctx, rlm_passwd_t *inst, REQUEST *request,
                       VALUE_PAIR **vps, struct mypasswd * pw, char when, char const *listname)
 {
        uint32_t i;
@@ -526,7 +525,7 @@ static void addresult (TALLOC_CTX *ctx, struct passwd_instance *inst, REQUEST *r
        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);
+                               vp = fr_pair_make(ctx, vps, inst->pwdfmt->field[i], pw->field[i], T_OP_EQ);
                                if (vp) {
                                        RDEBUG("Added %s: '%s' to %s ", inst->pwdfmt->field[i], pw->field[i], listname);
                                }
@@ -538,13 +537,13 @@ static void addresult (TALLOC_CTX *ctx, struct passwd_instance *inst, REQUEST *r
 
 static rlm_rcode_t CC_HINT(nonnull) mod_passwd_map(void *instance, REQUEST *request)
 {
-#define inst ((struct passwd_instance *)instance)
+#define inst ((rlm_passwd_t *)instance)
        char buffer[1024];
        VALUE_PAIR *key, *i;
        struct mypasswd * pw, *last_found;
        vp_cursor_t cursor;
 
-       key = pair_find_by_da(request->packet->vps, inst->keyattr, TAG_ANY);
+       key = fr_pair_find_by_da(request->packet->vps, inst->keyattr, TAG_ANY);
        if (!key) {
                return RLM_MODULE_NOTFOUND;
        }
@@ -560,7 +559,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_passwd_map(void *instance, REQUEST *requ
                        continue;
                }
                do {
-                       addresult(request, inst, request, &request->config_items, pw, 0, "config_items");
+                       addresult(request, inst, request, &request->config, pw, 0, "config");
                        addresult(request->reply, inst, request, &request->reply->vps, pw, 1, "reply_items");
                        addresult(request->packet, inst, request, &request->packet->vps, pw, 2, "request_items");
                } while ((pw = get_next(buffer, inst->ht, &last_found)));
@@ -577,25 +576,22 @@ static rlm_rcode_t CC_HINT(nonnull) mod_passwd_map(void *instance, REQUEST *requ
 
 extern module_t rlm_passwd;
 module_t rlm_passwd = {
-       RLM_MODULE_INIT,
-       "passwd",
-       RLM_TYPE_HUP_SAFE,      /* type */
-       sizeof(struct passwd_instance),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_passwd_map,         /* authorization */
-               NULL,                   /* pre-accounting */
-               mod_passwd_map,         /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_passwd_map          /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "passwd",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_passwd_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_passwd_map,
+               [MOD_ACCOUNTING]        = mod_passwd_map,
+               [MOD_POST_AUTH]         = mod_passwd_map,
+               [MOD_PRE_PROXY]         = mod_passwd_map,
+               [MOD_POST_PROXY]        = mod_passwd_map,
 #ifdef WITH_COA
-               , mod_passwd_map,
-               mod_passwd_map
+               [MOD_RECV_COA]          = mod_passwd_map,
+               [MOD_SEND_COA]          = mod_passwd_map
 #endif
        },
 };
index f5ca335..2f9eb23 100644 (file)
@@ -29,7 +29,7 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #ifdef INADDR_ANY
-#undef INADDR_ANY
+#  undef INADDR_ANY
 #endif
 #include <EXTERN.h>
 #include <perl.h>
@@ -74,7 +74,7 @@ typedef struct rlm_perl_t {
        char const      *xlat_name;
        char const      *perl_flags;
        PerlInterpreter *perl;
-       bool             perl_parsed;
+       bool            perl_parsed;
        pthread_key_t   *thread_key;
 
 #ifdef USE_ITHREADS
@@ -116,8 +116,7 @@ static const CONF_PARSER module_config[] = {
        { "func_start_accounting", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_perl_t, func_start_accounting), 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 */
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -126,8 +125,8 @@ static const CONF_PARSER module_config[] = {
 EXTERN_C void boot_DynaLoader(pTHX_ CV* cv);
 
 #ifdef USE_ITHREADS
-#define dl_librefs "DynaLoader::dl_librefs"
-#define dl_modules "DynaLoader::dl_modules"
+#  define dl_librefs "DynaLoader::dl_librefs"
+#  define dl_modules "DynaLoader::dl_modules"
 static void rlm_perl_clear_handles(pTHX)
 {
        AV *librefs = get_av(dl_librefs, false);
@@ -151,20 +150,16 @@ static void **rlm_perl_get_handles(pTHX)
 
        handles = (void **)rad_malloc(sizeof(void *) * (AvFILL(librefs)+2));
 
-       for (i=0; i<=AvFILL(librefs); i++) {
+       for (i = 0; i <= AvFILL(librefs); i++) {
                void *handle;
                SV *handle_sv = *av_fetch(librefs, i, false);
-
-               if(!handle_sv) {
-                       ERROR("Could not fetch $%s[%d]!\n",
-                              dl_librefs, (int)i);
+               if (!handle_sv) {
+                       ERROR("Could not fetch $%s[%d]!", dl_librefs, (int)i);
                        continue;
                }
                handle = (void *)SvIV(handle_sv);
 
-               if (handle) {
-                       handles[i] = handle;
-               }
+               if (handle) handles[i] = handle;
        }
 
        av_clear(modules);
@@ -183,8 +178,8 @@ static void rlm_perl_close_handles(void **handles)
                return;
        }
 
-       for (i=0; handles[i]; i++) {
-               DEBUG("close %p\n", handles[i]);
+       for (i = 0; handles[i]; i++) {
+               DEBUG("Close %p", handles[i]);
                dlclose(handles[i]);
        }
 
@@ -210,7 +205,7 @@ static void rlm_perl_destruct(PerlInterpreter *perl)
         * FIXME: This shouldn't happen
         *
         */
-       while (PL_scopestack_ix > 1 ){
+       while (PL_scopestack_ix > 1{
                LEAVE;
        }
 
@@ -253,9 +248,9 @@ static PerlInterpreter *rlm_perl_clone(PerlInterpreter *perl, pthread_key_t *key
        {
                dTHXa(interp);
        }
-#if PERL_REVISION >= 5 && PERL_VERSION <8
+#  if PERL_REVISION >= 5 && PERL_VERSION <8
        call_pv("CLONE",0);
-#endif
+#  endif
        ptr_table_free(PL_ptr_table);
        PL_ptr_table = NULL;
 
@@ -275,9 +270,9 @@ static PerlInterpreter *rlm_perl_clone(PerlInterpreter *perl, pthread_key_t *key
 #endif
 
 /*
- * This is wrapper for radlog
- * Now users can call radiusd::radlog(level,msg) wich is the same
- * calling radlog from C code.
+ *     This is wrapper for radlog
+ *     Now users can call radiusd::radlog(level,msg) wich is the same
+ *     calling radlog from C code.
  */
 static XS(XS_radiusd_radlog)
 {
@@ -311,7 +306,7 @@ static void xs_init(pTHX)
 }
 
 /*
- * The xlat function
+ *     The xlat function
  */
 static ssize_t perl_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
 {
@@ -458,6 +453,20 @@ static void perl_parse_config(CONF_SECTION *cs, int lvl, HV *rad_hv)
        DEBUG("%*s}", indent_section, " ");
 }
 
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
+{
+       rlm_perl_t      *inst = instance;
+
+       char const      *xlat_name;
+
+       xlat_name = cf_section_name2(conf);
+       if (!xlat_name) xlat_name = cf_section_name1(conf);
+
+       xlat_register(xlat_name, perl_xlat, NULL, inst);
+
+       return 0;
+}
+
 /*
  *     Do any per-module initialization that is separate to each
  *     configured instance of the module.  e.g. set up connections
@@ -474,24 +483,21 @@ static void perl_parse_config(CONF_SECTION *cs, int lvl, HV *rad_hv)
  */
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
-       rlm_perl_t       *inst = 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;
+       char const      **embed_c;      /* Stupid Perl and lack of const consistency */
+       char            **embed;
+       char            **envp = NULL;
+       int             exitstatus = 0, argc=0;
+       char            arg[] = "0";
 
-       CONF_SECTION *cs;
+       CONF_SECTION    *cs;
 
-       MEM(embed_c = talloc_zero_array(inst, char const *, 4));
-       memcpy(&embed, &embed_c, sizeof(embed));
+#ifdef USE_ITHREADS
        /*
         *      Create pthread key. This key will be stored in instance
         */
-
-#ifdef USE_ITHREADS
        pthread_mutex_init(&inst->clone_mutex, NULL);
 
        inst->thread_key = rad_malloc(sizeof(*inst->thread_key));
@@ -500,8 +506,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        rlm_perl_make_key(inst->thread_key);
 #endif
 
-       char arg[] = "0";
-
+       /*
+        *      Setup the argument array we pass to the perl interpreter
+        */
+       MEM(embed_c = talloc_zero_array(inst, char const *, 4));
+       memcpy(&embed, &embed_c, sizeof(embed));
        embed_c[0] = NULL;
        if (inst->perl_flags) {
                embed_c[1] = inst->perl_flags;
@@ -514,14 +523,20 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                argc = 3;
        }
 
+       /*
+        *      Create tweak the server's environment to support
+        *      perl. Docs say only call this once... Oops.
+        */
        PERL_SYS_INIT3(&argc, &embed, &envp);
 
+       /*
+        *      Allocate a new perl interpreter to do the parsing
+        */
        if ((inst->perl = perl_alloc()) == NULL) {
                ERROR("rlm_perl: No memory for allocating new perl !");
-               return (-1);
+               return -1;
        }
-
-       perl_construct(inst->perl);
+       perl_construct(inst->perl);     /* ...and initialise it */
 
 #ifdef USE_ITHREADS
        PL_perl_destruct_level = 2;
@@ -536,32 +551,21 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
 #endif
 
-       xlat_name = cf_section_name2(conf);
-       if (!xlat_name)
-               xlat_name = cf_section_name1(conf);
-       if (xlat_name) {
-               xlat_register(xlat_name, perl_xlat, NULL, inst);
-       }
-
        exitstatus = perl_parse(inst->perl, xs_init, argc, embed, NULL);
 
        end_AV = PL_endav;
        PL_endav = (AV *)NULL;
 
        if (exitstatus) {
-               ERROR("rlm_perl: perl_parse failed: %s not found or has syntax errors. \n", inst->module);
+               ERROR("rlm_perl: perl_parse failed: %s not found or has syntax errors", inst->module);
                return -1;
        }
 
        /* parse perl configuration sub-section */
        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);
+               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);
        }
 
        inst->perl_parsed = true;
@@ -612,7 +616,7 @@ static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR
        vp_cursor_t cursor;
 
        RINDENT();
-       pairsort(vps, attrtagcmp);
+       fr_pair_list_sort(vps, fr_pair_cmp_by_da_tag);
        for (vp = fr_cursor_init(&cursor, vps);
             vp;
             vp = fr_cursor_next(&cursor)) {
@@ -687,13 +691,13 @@ static void perl_store_vps(UNUSED TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR
 static int pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op,
                      const char *hash_name, const char *list_name)
 {
-       char        *val;
+       char            *val;
        VALUE_PAIR      *vp;
 
        if (SvOK(sv)) {
                STRLEN len;
                val = SvPV(sv, len);
-               vp = pairmake(ctx, vps, key, NULL, op);
+               vp = fr_pair_make(ctx, vps, key, NULL, op);
                if (!vp) {
                fail:
                        REDEBUG("Failed to create pair %s:%s %s %s", list_name, key,
@@ -703,11 +707,11 @@ static int pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char
 
                switch (vp->da->type) {
                case PW_TYPE_STRING:
-                       pairstrncpy(vp, val, len);
+                       fr_pair_value_bstrncpy(vp, val, len);
                        break;
 
                default:
-                       if (pairparsevalue(vp, val, len) < 0) goto fail;
+                       if (fr_pair_value_from_str(vp, val, len) < 0) goto fail;
                }
 
                RDEBUG("&%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens, op, "<INVALID>"),
@@ -762,6 +766,7 @@ static int do_perl(void *instance, REQUEST *request, char const *function_name)
        HV              *rad_check_hv;
        HV              *rad_config_hv;
        HV              *rad_request_hv;
+       HV              *rad_state_hv;
 #ifdef WITH_PROXY
        HV              *rad_request_proxy_hv;
        HV              *rad_request_proxy_reply_hv;
@@ -799,11 +804,13 @@ static int do_perl(void *instance, REQUEST *request, char const *function_name)
                rad_check_hv = get_hv("RAD_CHECK", 1);
                rad_config_hv = get_hv("RAD_CONFIG", 1);
                rad_request_hv = get_hv("RAD_REQUEST", 1);
+               rad_state_hv = get_hv("RAD_STATE", 1);
 
                perl_store_vps(request->packet, request, &request->packet->vps, rad_request_hv, "RAD_REQUEST", "request");
                perl_store_vps(request->reply, request, &request->reply->vps, rad_reply_hv, "RAD_REPLY", "reply");
-               perl_store_vps(request, request, &request->config_items, rad_check_hv, "RAD_CHECK", "control");
-               perl_store_vps(request, request, &request->config_items, rad_config_hv, "RAD_CONFIG", "control");
+               perl_store_vps(request, request, &request->config, rad_check_hv, "RAD_CHECK", "control");
+               perl_store_vps(request, request, &request->config, rad_config_hv, "RAD_CONFIG", "control");
+               perl_store_vps(request, request, &request->state, rad_state_hv, "RAD_STATE", "session-state");
 
 #ifdef WITH_PROXY
                rad_request_proxy_hv = get_hv("RAD_REQUEST_PROXY",1);
@@ -838,9 +845,8 @@ static int do_perl(void *instance, REQUEST *request, char const *function_name)
                SPAGAIN;
 
                if (SvTRUE(ERRSV)) {
-                       ERROR("rlm_perl: perl_embed:: module = %s , func = %s exit status= %s\n",
-                              inst->module,
-                              function_name, SvPV(ERRSV,n_a));
+                       RDEBUG("perl_embed:: module = %s , func = %s exit status= %s\n",
+                              inst->module, function_name, SvPV(ERRSV,n_a));
                        (void)POPs;
                }
 
@@ -858,28 +864,34 @@ static int do_perl(void *instance, REQUEST *request, char const *function_name)
 
                vp = NULL;
                if ((get_hv_content(request->packet, request, rad_request_hv, &vp, "RAD_REQUEST", "request")) == 0) {
-                       pairfree(&request->packet->vps);
+                       fr_pair_list_free(&request->packet->vps);
                        request->packet->vps = vp;
                        vp = NULL;
 
                        /*
                         *      Update cached copies
                         */
-                       request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
-                       request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+                       request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+                       request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
                        if (!request->password)
-                               request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
+                               request->password = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
                }
 
                if ((get_hv_content(request->reply, request, rad_reply_hv, &vp, "RAD_REPLY", "reply")) == 0) {
-                       pairfree(&request->reply->vps);
+                       fr_pair_list_free(&request->reply->vps);
                        request->reply->vps = vp;
                        vp = NULL;
                }
 
                if ((get_hv_content(request, request, rad_check_hv, &vp, "RAD_CHECK", "control")) == 0) {
-                       pairfree(&request->config_items);
-                       request->config_items = vp;
+                       fr_pair_list_free(&request->config);
+                       request->config = vp;
+                       vp = NULL;
+               }
+
+               if ((get_hv_content(request, request, rad_state_hv, &vp, "RAD_STATE", "session-state")) == 0) {
+                       fr_pair_list_free(&request->state);
+                       request->state = vp;
                        vp = NULL;
                }
 
@@ -887,7 +899,7 @@ static int do_perl(void *instance, REQUEST *request, char const *function_name)
                if (request->proxy &&
                    (get_hv_content(request->proxy, request, rad_request_proxy_hv, &vp,
                                    "RAD_REQUEST_PROXY", "proxy-request") == 0)) {
-                       pairfree(&request->proxy->vps);
+                       fr_pair_list_free(&request->proxy->vps);
                        request->proxy->vps = vp;
                        vp = NULL;
                }
@@ -895,7 +907,7 @@ static int do_perl(void *instance, REQUEST *request, char const *function_name)
                if (request->proxy_reply &&
                    (get_hv_content(request->proxy_reply, request, rad_request_proxy_reply_hv, &vp,
                                    "RAD_REQUEST_PROXY_REPLY", "proxy-reply") == 0)) {
-                       pairfree(&request->proxy_reply->vps);
+                       fr_pair_list_free(&request->proxy_reply->vps);
                        request->proxy_reply->vps = vp;
                        vp = NULL;
                }
@@ -937,10 +949,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        VALUE_PAIR      *pair;
        int             acctstatustype=0;
 
-       if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL) {
+       if ((pair = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL) {
                acctstatustype = pair->vp_integer;
        } else {
-               ERROR("Invalid Accounting Packet");
+               RDEBUG("Invalid Accounting Packet");
                return RLM_MODULE_INVALID;
        }
 
@@ -979,38 +991,7 @@ static int mod_detach(void *instance)
        rlm_perl_t      *inst = (rlm_perl_t *) instance;
        int             exitstatus = 0, count = 0;
 
-       if (inst->rad_perlconf_hv != NULL) {
-               hv_undef(inst->rad_perlconf_hv);
-       }
-
-#if 0
-       /*
-        *      FIXME: Call this in the destruct function?
-        */
-       {
-               dTHXa(handle->clone);
-               PERL_SET_CONTEXT(handle->clone);
-               {
-                       dSP; ENTER; SAVETMPS; PUSHMARK(SP);
-                       count = call_pv(inst->func_detach, G_SCALAR | G_EVAL );
-                       SPAGAIN;
-
-                       if (count == 1) {
-                               exitstatus = POPi;
-                               /*
-                                * FIXME: bug in perl
-                                *
-                                */
-                               if (exitstatus >= 100 || exitstatus < 0) {
-                                       exitstatus = RLM_MODULE_FAIL;
-                               }
-                       }
-                       PUTBACK;
-                       FREETMPS;
-                       LEAVE;
-               }
-       }
-#endif
+       if (inst->rad_perlconf_hv != NULL) hv_undef(inst->rad_perlconf_hv);
 
        if (inst->perl_parsed && inst->func_detach) {
                dTHXa(inst->perl);
@@ -1058,33 +1039,32 @@ DIAG_ON(nested-externs)
  */
 extern module_t rlm_perl;
 module_t rlm_perl = {
-       RLM_MODULE_INIT,
-       "perl",                         /* Name */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "perl",
 #ifdef USE_ITHREADS
-       RLM_TYPE_THREAD_SAFE,           /* type */
+       .type           = RLM_TYPE_THREAD_SAFE,
 #else
-       RLM_TYPE_THREAD_UNSAFE,
+       .type           = RLM_TYPE_THREAD_UNSAFE,
 #endif
-       sizeof(rlm_perl_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authenticate */
-               mod_authorize,          /* authorize */
-               mod_preacct,            /* preacct */
-               mod_accounting, /* accounting */
-               mod_checksimul,         /* check simul */
+       .inst_size      = sizeof(rlm_perl_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preacct,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_SESSION]           = mod_checksimul,
 #ifdef WITH_PROXY
-               mod_pre_proxy,          /* pre-proxy */
-               mod_post_proxy, /* post-proxy */
-#else
-               NULL, NULL,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
+               [MOD_POST_PROXY]        = mod_post_proxy,
 #endif
-               mod_post_auth           /* post-auth */
+               [MOD_POST_AUTH]         = mod_post_auth,
 #ifdef WITH_COA
-               , mod_recv_coa,
-               mod_send_coa
+               [MOD_RECV_COA]          = mod_recv_coa,
+               [MOD_SEND_COA]          = mod_send_coa
 #endif
        },
 };
index 149abcd..ce75ac8 100644 (file)
@@ -57,7 +57,7 @@ static const CONF_PARSER module_config[] = {
 #if 0
        { "with_cablelabs_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_cablelabs_vsa_hack), NULL },
 #endif
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -66,7 +66,7 @@ static const CONF_PARSER module_config[] = {
 static int fall_through(VALUE_PAIR *vp)
 {
        VALUE_PAIR *tmp;
-       tmp = pairfind(vp, PW_FALL_THROUGH, 0, TAG_ANY);
+       tmp = fr_pair_find_by_num(vp, PW_FALL_THROUGH, 0, TAG_ANY);
 
        return tmp ? tmp->vp_integer : 0;
 }
@@ -147,7 +147,7 @@ static void cisco_vsa_hack(REQUEST *request)
                        gettoken(&p, newattr, sizeof(newattr), false);
 
                        if (dict_attrbyname(newattr) != NULL) {
-                               pairmake_packet(newattr, ptr + 1, T_OP_EQ);
+                               pair_make_request(newattr, ptr + 1, T_OP_EQ);
                        }
                } else {        /* h322-foo-bar = "h323-foo-bar = baz" */
                        /*
@@ -155,7 +155,7 @@ static void cisco_vsa_hack(REQUEST *request)
                         *      value field, we use only the value on
                         *      the right side of the '=' character.
                         */
-                       pairstrcpy(vp, ptr + 1);
+                       fr_pair_value_strcpy(vp, ptr + 1);
                }
        }
 }
@@ -242,7 +242,7 @@ static void cablelabs_vsa_hack(VALUE_PAIR **list)
 {
        VALUE_PAIR *ev;
 
-       ev = pairfind(*list, 1, 4491, TAG_ANY); /* Cablelabs-Event-Message */
+       ev = fr_pair_find_by_num(*list, 1, 4491, TAG_ANY); /* Cablelabs-Event-Message */
        if (!ev) {
                return;
        }
@@ -269,7 +269,7 @@ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
         *      If it isn't there, then we can't mangle the request.
         */
        request_pairs = request->packet->vps;
-       namepair = pairfind(request_pairs, PW_USER_NAME, 0, TAG_ANY);
+       namepair = fr_pair_find_by_num(request_pairs, PW_USER_NAME, 0, TAG_ANY);
        if (!namepair || (namepair->vp_length == 0)) {
                return;
        }
@@ -287,7 +287,7 @@ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
                if ((ptr = strchr(namepair->vp_strvalue, '\\')) != NULL) {
                        strlcpy(newname, ptr + 1, sizeof(newname));
                        /* Same size */
-                       pairstrcpy(namepair, newname);
+                       fr_pair_value_strcpy(namepair, newname);
                }
        }
 
@@ -302,7 +302,7 @@ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
                 */
                if ((strlen(namepair->vp_strvalue) > 10) &&
                    (namepair->vp_strvalue[10] == '/')) {
-                       pairstrcpy(namepair, namepair->vp_strvalue + 11);
+                       fr_pair_value_strcpy(namepair, namepair->vp_strvalue + 11);
                }
        }
 
@@ -310,9 +310,9 @@ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
         *      Small check: if Framed-Protocol present but Service-Type
         *      is missing, add Service-Type = Framed-User.
         */
-       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->packet, &request->packet->vps, PW_SERVICE_TYPE, 0);
+       if (fr_pair_find_by_num(request_pairs, PW_FRAMED_PROTOCOL, 0, TAG_ANY) != NULL &&
+           fr_pair_find_by_num(request_pairs, PW_SERVICE_TYPE, 0, TAG_ANY) == NULL) {
+               tmp = radius_pair_create(request->packet, &request->packet->vps, PW_SERVICE_TYPE, 0);
                tmp->vp_integer = PW_FRAMED_USER;
        }
 
@@ -354,11 +354,11 @@ static int hunt_paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check)
        for (check_item = fr_cursor_init(&cursor, &check);
             check_item && (result != 0);
             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);
+               /* FIXME: fr_pair_list_copy should be removed once VALUE_PAIRs are no longer in linked lists */
+               tmp = fr_pair_copy(request, check_item);
                tmp->op = check_item->op;
                result = paircompare(req, request, check_item, NULL);
-               pairfree(&tmp);
+               fr_pair_list_free(&tmp);
        }
 
        return result;
@@ -386,7 +386,7 @@ static int hints_setup(PAIR_LIST *hints, REQUEST *request)
        /*
         *      Check for valid input, zero length names not permitted
         */
-       name = (tmp = pairfind(request_pairs, PW_USER_NAME, 0, TAG_ANY)) ?
+       name = (tmp = fr_pair_find_by_num(request_pairs, PW_USER_NAME, 0, TAG_ANY)) ?
                tmp->vp_strvalue : NULL;
        if (!name || name[0] == 0) {
                /*
@@ -407,11 +407,11 @@ static int hints_setup(PAIR_LIST *hints, REQUEST *request)
                         *      except PW_STRIP_USER_NAME and PW_FALL_THROUGH
                         *      and xlat them.
                         */
-                       add = paircopy(request->packet, i->reply);
+                       add = fr_pair_list_copy(request->packet, i->reply);
                        ft = fall_through(add);
 
-                       pairdelete(&add, PW_STRIP_USER_NAME, 0, TAG_ANY);
-                       pairdelete(&add, PW_FALL_THROUGH, 0, TAG_ANY);
+                       fr_pair_delete_by_num(&add, PW_STRIP_USER_NAME, 0, TAG_ANY);
+                       fr_pair_delete_by_num(&add, PW_FALL_THROUGH, 0, TAG_ANY);
                        radius_pairmove(request, &request->packet->vps, add, true);
 
                        updated = 1;
@@ -464,10 +464,10 @@ static int huntgroup_access(REQUEST *request, PAIR_LIST *huntgroups)
                         *  We've matched the huntgroup, so add it in
                         *  to the list of request pairs.
                         */
-                       vp = pairfind(request_pairs, PW_HUNTGROUP_NAME, 0, TAG_ANY);
+                       vp = fr_pair_find_by_num(request_pairs, PW_HUNTGROUP_NAME, 0, TAG_ANY);
                        if (!vp) {
-                               vp = radius_paircreate(request->packet, &request->packet->vps, PW_HUNTGROUP_NAME, 0);
-                               pairstrcpy(vp, i->name);
+                               vp = radius_pair_create(request->packet, &request->packet->vps, PW_HUNTGROUP_NAME, 0);
+                               fr_pair_value_strcpy(vp, i->name);
                        }
                        r = RLM_MODULE_OK;
                }
@@ -487,17 +487,17 @@ static int add_nas_attr(REQUEST *request)
 
        switch (request->packet->src_ipaddr.af) {
        case AF_INET:
-               nas = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
+               nas = fr_pair_find_by_num(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
                if (!nas) {
-                       nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IP_ADDRESS, 0);
+                       nas = radius_pair_create(request->packet, &request->packet->vps, PW_NAS_IP_ADDRESS, 0);
                        nas->vp_ipaddr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
                }
                break;
 
        case AF_INET6:
-               nas = pairfind(request->packet->vps, PW_NAS_IPV6_ADDRESS, 0, TAG_ANY);
+               nas = fr_pair_find_by_num(request->packet->vps, PW_NAS_IPV6_ADDRESS, 0, TAG_ANY);
                if (!nas) {
-                       nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IPV6_ADDRESS, 0);
+                       nas = radius_pair_create(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));
                }
@@ -569,7 +569,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                 *      in place, to go from Ascend's weird values to something
                 *      approaching rationality.
                 */
-               ascend_nasport_hack(pairfind(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY),
+               ascend_nasport_hack(fr_pair_find_by_num(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY),
                                    inst->ascend_channels_per_line);
        }
 
@@ -601,9 +601,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         *      Add an event timestamp. Means Event-Timestamp can be used
         *      consistently instead of one letter expansions.
         */
-       vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
        if (!vp) {
-               vp = radius_paircreate(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
+               vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
                vp->vp_date = request->packet->timestamp.tv_sec;
        }
 
@@ -624,10 +624,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         *      is PW_CHAP_CHALLENGE we need to add it so that other
         *      modules can use it as a normal attribute.
         */
-       if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
-           pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
-               vp = radius_paircreate(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
-               pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
+       if (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
+           fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
+               vp = radius_pair_create(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
+               fr_pair_value_memcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
        }
 
        if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
@@ -695,14 +695,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *r
         *      the server can use it, rather than various error-prone
         *      manual calculations.
         */
-       vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
        if (!vp) {
                VALUE_PAIR *delay;
 
-               vp = radius_paircreate(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
+               vp = radius_pair_create(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);
+               delay = fr_pair_find_by_num(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
                if (delay) {
                        if ((delay->vp_integer >= vp->vp_date) || (delay->vp_integer == UINT32_MAX)) {
                                RWARN("Ignoring invalid Acct-Delay-time of %u seconds", delay->vp_integer);
@@ -726,22 +726,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *r
 /* globally exported name */
 extern module_t rlm_preprocess;
 module_t rlm_preprocess = {
-       RLM_MODULE_INIT,
-       "preprocess",
-       0,              /* type */
-       sizeof(rlm_preprocess_t),
-       module_config,
-       mod_instantiate,                        /* instantiation */
-       NULL,                                   /* detach */
-       {
-               NULL,                           /* authentication */
-               mod_authorize,                  /* authorization */
-               mod_preaccounting,              /* pre-accounting */
-               NULL,                           /* accounting */
-               NULL,                           /* checksimul */
-               NULL,                           /* pre-proxy */
-               NULL,                           /* post-proxy */
-               NULL                            /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "preprocess",
+       .inst_size      = sizeof(rlm_preprocess_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preaccounting
        },
 };
 
index 5c1e96d..22456a0 100644 (file)
@@ -102,8 +102,7 @@ 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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static struct {
@@ -343,7 +342,7 @@ static void mod_vptuple(TALLOC_CTX *ctx, VALUE_PAIR **vps, PyObject *pValue,
                }
                s1 = PyString_AsString(pStr1);
                s2 = PyString_AsString(pStr2);
-               vp = pairmake(ctx, vps, s1, s2, op);
+               vp = fr_pair_make(ctx, vps, s1, s2, op);
                if (vp != NULL) {
                        DEBUG("rlm_python:%s: '%s' = '%s'", funcname, s1, s2);
                } else {
@@ -546,7 +545,7 @@ static rlm_rcode_t do_python(rlm_python_t *inst, REQUEST *request, PyObject *pFu
                mod_vptuple(request->reply, &request->reply->vps,
                            PyTuple_GET_ITEM(pRet, 1), funcname);
                /* Config item tuple */
-               mod_vptuple(request, &request->config_items,
+               mod_vptuple(request, &request->config,
                            PyTuple_GET_ITEM(pRet, 2), funcname);
 
        } else if (PyInt_CheckExact(pRet)) {
@@ -747,25 +746,25 @@ A(send_coa)
  */
 extern module_t rlm_python;
 module_t rlm_python = {
-       RLM_MODULE_INIT,
-       "python",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_python_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,  /* authorization */
-               mod_preacct,            /* preaccounting */
-               mod_accounting, /* accounting */
-               mod_checksimul, /* checksimul */
-               mod_pre_proxy,  /* pre-proxy */
-               mod_post_proxy, /* post-proxy */
-               mod_post_auth   /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "python",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_python_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preacct,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_SESSION]           = mod_checksimul,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
+               [MOD_POST_PROXY]        = mod_post_proxy,
+               [MOD_POST_AUTH]         = mod_post_auth,
 #ifdef WITH_COA
-               , mod_recv_coa,
-               mod_send_coa
+               [MOD_RECV_COA]          = mod_recv_coa,
+               [MOD_SEND_COA]          = mod_send_coa
 #endif
        }
 };
index 788a90f..b542618 100644 (file)
@@ -66,7 +66,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -174,7 +174,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        /*
         *      Which type is this.
         */
-       if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
                RDEBUG("No Accounting-Status-Type record");
                return RLM_MODULE_NOOP;
        }
@@ -197,10 +197,10 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
                int check1 = 0;
                int check2 = 0;
 
-               if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY))
+               if ((vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY))
                     == NULL || vp->vp_date == 0)
                        check1 = 1;
-               if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID, 0, TAG_ANY))
+               if ((vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_SESSION_ID, 0, TAG_ANY))
                     != NULL && vp->vp_length == 8 &&
                     memcmp(vp->vp_strvalue, "00000000", 8) == 0)
                        check2 = 1;
@@ -639,11 +639,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST *requ
        /*
         *      Setup some stuff, like for MPP detection.
         */
-       if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
                ipno = vp->vp_ipaddr;
        }
 
-       if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
                call_num = vp->vp_strvalue;
        }
 
@@ -741,30 +741,18 @@ static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST *requ
 /* globally exported name */
 extern module_t rlm_radutmp;
 module_t rlm_radutmp = {
-       RLM_MODULE_INIT,
-       "radutmp",
-       RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_HUP_SAFE,     /* type */
-       sizeof(rlm_radutmp_t),
-       module_config,
-       NULL,                          /* instantiation */
-       NULL,                          /* detach */
-       {
-               NULL,            /* authentication */
-               NULL,            /* authorization */
-               NULL,            /* preaccounting */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "radutmp",
+       .type           = RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_radutmp_t),
+       .config         = module_config,
+       .methods = {
 #ifdef WITH_ACCOUNTING
-               mod_accounting,   /* accounting */
-#else
-               NULL,
+               [MOD_ACCOUNTING]        = mod_accounting,
 #endif
 #ifdef WITH_SESSION_MGMT
-               mod_checksimul, /* checksimul */
-#else
-               NULL,
+               [MOD_SESSION]           = mod_checksimul
 #endif
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
        },
 };
 
index 08beb03..3498095 100644 (file)
@@ -47,19 +47,17 @@ typedef struct rlm_realm_t {
 } rlm_realm_t;
 
 static CONF_PARSER module_config[] = {
-  { "format", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t, format_string), "suffix" },
-  { "delimiter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t, delim), "@" },
-  { "ignore_default", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_realm_t, ignore_default), "no" },
-  { "ignore_null", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_realm_t, ignore_null), "no" },
-
+       { "format", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t, format_string), "suffix" },
+       { "delimiter", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t, delim), "@" },
+       { "ignore_default", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_realm_t, ignore_default), "no" },
+       { "ignore_null", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_realm_t, ignore_null), "no" },
 #ifdef HAVE_TRUST_ROUTER_TR_DH_H
-  { "default_community", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,default_community),  "none" },
-  { "rp_realm", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,rp_realm),  "none" },
-  { "trust_router", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,trust_router),  "none" },
-  { "tr_port", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_realm_t,tr_port),  "0" },
+       { "default_community", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,default_community),  "none" },
+       { "rp_realm", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,rp_realm),  "none" },
+       { "trust_router", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_realm_t,trust_router),  "none" },
+       { "tr_port", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_realm_t,tr_port),  "0" },
 #endif
-
-  { NULL, -1, 0, NULL, NULL }    /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -107,7 +105,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
         *      it already ( via another rlm_realm instance ) and should return.
         */
 
-       if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL ) {
+       if (fr_pair_find_by_num(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL ) {
                RDEBUG2("Request already has destination realm set.  Ignoring");
                return RLM_MODULE_NOOP;
        }
@@ -168,13 +166,15 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
         *      Allow DEFAULT realms unless told not to.
         */
        realm = realm_find(realmname);
+
 #ifdef HAVE_TRUST_ROUTER_TR_DH_H
        /*
         *      Try querying for the dynamic realm.
         */
-       if (!realm)
-         realm = tr_query_realm(request, realmname, inst->default_community, inst->rp_realm, inst->trust_router, inst->tr_port);
+       if (!realm && inst->trust_router)
+               realm = tr_query_realm(request, realmname, inst->default_community, inst->rp_realm, inst->trust_router, inst->tr_port);
 #endif
+
        if (!realm) {
                RDEBUG2("No such realm \"%s\"", (!realmname) ? "NULL" : realmname);
                talloc_free(namebuf);
@@ -199,7 +199,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->packet, &request->packet->vps,
+                       vp = radius_pair_create(request->packet, &request->packet->vps,
                                               PW_STRIPPED_USER_NAME, 0);
                        RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
                } else {
@@ -207,7 +207,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
                        RDEBUG2("Setting Stripped-User-Name = \"%s\"", username);
                }
 
-               pairstrcpy(vp, username);
+               fr_pair_value_strcpy(vp, username);
                request->username = vp;
        }
 
@@ -220,7 +220,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
         *      entered.
         */
        if (realm->name[0] != '~') realmname = realm->name;
-       pairmake_packet("Realm", realmname, T_OP_EQ);
+       pair_make_request("Realm", realmname, T_OP_EQ);
        RDEBUG2("Adding Realm = \"%s\"", realmname);
 
        talloc_free(namebuf);
@@ -282,12 +282,13 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
         *      that has already proxied the request, we don't need to do
         *      it again.
         */
-       vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY);
        if (vp && (request->packet->src_ipaddr.af == AF_INET)) {
                int i;
                fr_ipaddr_t my_ipaddr;
 
                my_ipaddr.af = AF_INET;
+               my_ipaddr.prefix = 32;
                my_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
 
                /*
@@ -298,8 +299,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
                 *      send it there again.
                 */
                for (i = 0; i < realm->acct_pool->num_home_servers; i++) {
-                       if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr,
-                                           &my_ipaddr) == 0) {
+                       if (fr_ipaddr_cmp(&realm->acct_pool->servers[i]->ipaddr, &my_ipaddr) == 0) {
                                RDEBUG2("Suppressing proxy due to FreeRADIUS-Proxied-To");
                                return RLM_MODULE_OK;
                        }
@@ -374,7 +374,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
 #ifdef HAVE_TRUST_ROUTER_TR_DH_H
        /* initialize the trust router integration code */
-       if (!tr_init()) return -1;
+       if (strcmp(inst->trust_router, "none") != 0) {
+               if (!tr_init()) return -1;
+       } else {
+               rad_const_free(&inst->trust_router);
+       }
 #endif
 
        return 0;
@@ -407,7 +411,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         */
        RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
               realm->name);
-       pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
+       pair_make_config("Proxy-To-Realm", realm->name, T_OP_EQ);
 
        return RLM_MODULE_UPDATED; /* try the next module */
 }
@@ -439,7 +443,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_preacct(void *instance, REQUEST *request
         */
        RDEBUG2("Preparing to proxy accounting request to realm \"%s\"\n",
               realm->name);
-       pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
+       pair_make_config("Proxy-To-Realm", realm->name, T_OP_EQ);
 
        return RLM_MODULE_UPDATED; /* try the next module */
 }
@@ -454,12 +458,12 @@ 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) {
+       if (fr_pair_find_by_num(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL) {
                RDEBUG2("Request already has destination realm set.  Ignoring");
                return RLM_MODULE_NOOP;
        }
 
-       vp = pairfind(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
        if (!vp) return RLM_MODULE_NOOP;
 
        /*
@@ -490,7 +494,7 @@ static rlm_rcode_t mod_realm_recv_coa(UNUSED void *instance, REQUEST *request)
         */
        RDEBUG2("Preparing to proxy authentication request to realm \"%s\"\n",
               realm->name);
-       pairmake_config("Proxy-To-Realm", realm->name, T_OP_EQ);
+       pair_make_config("Proxy-To-Realm", realm->name, T_OP_EQ);
 
        return RLM_MODULE_UPDATED; /* try the next module */
 }
@@ -499,25 +503,17 @@ static rlm_rcode_t mod_realm_recv_coa(UNUSED void *instance, REQUEST *request)
 /* globally exported name */
 extern module_t rlm_realm;
 module_t rlm_realm = {
-       RLM_MODULE_INIT,
-       "realm",
-       RLM_TYPE_HUP_SAFE,      /* type */
-       sizeof(struct rlm_realm_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,  /* authorization */
-               mod_preacct,            /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "realm",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(struct rlm_realm_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preacct,
 #ifdef WITH_COA
-               , mod_realm_recv_coa,   /* recv-coa */
-               NULL                    /* send-coa */
+               [MOD_RECV_COA]          = mod_realm_recv_coa
 #endif
        },
 };
index 2401662..1636a4b 100644 (file)
@@ -153,29 +153,8 @@ static home_server_t *srvr_blk_to_home_server(TALLOC_CTX *ctx,
 
        rad_assert(blk != NULL);
        tid_srvr_get_address(blk, &sa, &sa_len);
-       switch (sa->sa_family) {
-
-       case AF_INET: {
-               const struct sockaddr_in *sin = (const struct sockaddr_in *) sa;
-               home_server_ip.af = AF_INET;
-               home_server_ip.scope = 0;
-               home_server_ip.ipaddr.ip4addr = sin->sin_addr;
-               port = ntohs(sin->sin_port);
-               break;
-       }
-
-       case AF_INET6: {
-               const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *) sa;
-               home_server_ip.af = AF_INET6;
-               home_server_ip.scope = sin6->sin6_scope_id;
-               home_server_ip.ipaddr.ip6addr = sin6->sin6_addr;
-               break;
-       }
 
-       default:
-               DEBUG2("Unknown address family in tid srvr block");
-               return NULL;
-       }
+       fr_sockaddr2ipaddr((struct sockaddr_storage *) sa, sa_len, &home_server_ip, &port);
   
        if (0 != getnameinfo(sa, sa_len,
                             nametemp,
@@ -377,14 +356,16 @@ REALM *tr_query_realm(REQUEST *request, char const *realm,
 
        if (!realm) return NULL;
 
+       if (!trustrouter || (strcmp(trustrouter, "none") == 0)) return NULL;
+
        /* clear the cookie structure */
        memset (&cookie, 0, sizeof(cookie));
 
        /* See if the request overrides the community*/
-       vp = pairfind(request->packet->vps, PW_UKERNA_TR_COI, VENDORPEC_UKERNA, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_UKERNA_TR_COI, VENDORPEC_UKERNA, TAG_ANY);
        if (vp)
                community = vp->vp_strvalue;
-       else pairmake_packet("Trust-Router-COI", community, T_OP_SET);
+       else pair_make_request("Trust-Router-COI", community, T_OP_SET);
 
        cookie.fr_realm_name = talloc_asprintf(NULL,
                                               "%s%%%s",
@@ -420,8 +401,8 @@ REALM *tr_query_realm(REQUEST *request, char const *realm,
                DEBUG2("TID response is error, rc = %d: %s.\n", cookie.result,
                       cookie.err_msg?cookie.err_msg:"(NO ERROR TEXT)");
                if (cookie.err_msg) 
-                       pairmake_reply("Reply-Message", cookie.err_msg, T_OP_SET);
-               pairmake_reply("Error-Cause", "502", T_OP_SET); /*proxy unroutable*/
+                       pair_make_reply("Reply-Message", cookie.err_msg, T_OP_SET);
+               pair_make_reply("Error-Cause", "502", T_OP_SET); /*proxy unroutable*/
        }
 
 cleanup:
index 7da2676..689aaff 100644 (file)
@@ -36,13 +36,15 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static int _mod_conn_free(REDISSOCK *dissocket)
 {
-       redisFree(dissocket->conn);
+       if (dissocket->conn) {
+               redisFree(dissocket->conn);
+               dissocket->conn = NULL;
+       }
 
        if (dissocket->reply) {
                freeReplyObject(dissocket->reply);
@@ -61,7 +63,18 @@ static void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
        char buffer[1024];
 
        conn = redisConnect(inst->hostname, inst->port);
-       if (conn->err) return NULL;
+       if (!conn) {
+               ERROR("rlm_redis (%s): Failed calling redisConnect('%s', %d)",
+                     inst->xlat_name, inst->hostname, inst->port);
+               return NULL;
+       }
+
+       if (conn && conn->err) {
+               ERROR("rlm_redis (%s): Problems with redisConnect('%s', %d), %s",
+                               inst->xlat_name, inst->hostname, inst->port, redisReplyReaderGetError(conn));
+               redisFree(conn);
+               return NULL;
+       }
 
        if (inst->password) {
                snprintf(buffer, sizeof(buffer), "AUTH %s", inst->password);
@@ -103,7 +116,6 @@ static void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
                        goto do_close;
                }
 
-
                switch (reply->type) {
                case REDIS_REPLY_STATUS:
                        if (strcmp(reply->str, "OK") != 0) {
@@ -188,7 +200,7 @@ static int mod_detach(void *instance)
 {
        REDIS_INST *inst = instance;
 
-       fr_connection_pool_delete(inst->pool);
+       fr_connection_pool_free(inst->pool);
 
        return 0;
 }
@@ -201,7 +213,7 @@ int rlm_redis_query(REDISSOCK **dissocket_p, REDIS_INST *inst,
 {
        REDISSOCK *dissocket;
        int argc;
-       char *argv[MAX_REDIS_ARGS];
+       char const *argv[MAX_REDIS_ARGS];
        char argv_buf[MAX_QUERY_LEN];
 
        if (!query || !*query || !inst || !dissocket_p) {
@@ -215,8 +227,8 @@ int rlm_redis_query(REDISSOCK **dissocket_p, REDIS_INST *inst,
 
        dissocket = *dissocket_p;
 
-       DEBUG2("executing %s ...", argv[0]);
-       dissocket->reply = redisCommandArgv(dissocket->conn, argc, (char const **)(void **)argv, NULL);
+       DEBUG2("rlm_redis (%s): executing the query: \"%s\"", inst->xlat_name, query);
+       dissocket->reply = redisCommandArgv(dissocket->conn, argc, argv, NULL);
        if (!dissocket->reply) {
                RERROR("%s", dissocket->conn->errstr);
 
@@ -230,7 +242,7 @@ int rlm_redis_query(REDISSOCK **dissocket_p, REDIS_INST *inst,
                dissocket->reply = redisCommand(dissocket->conn, query);
                if (!dissocket->reply) {
                        RERROR("Failed after re-connect");
-                       fr_connection_del(inst->pool, dissocket);
+                       fr_connection_close(inst->pool, dissocket);
                        goto error;
                }
 
@@ -259,23 +271,23 @@ int rlm_redis_finish_query(REDISSOCK *dissocket)
        return 0;
 }
 
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
-       static bool version_done;
-
        REDIS_INST *inst = instance;
 
-       if (!version_done) {
-               version_done = true;
-
-               INFO("rlm_redis: libhiredis version: %i.%i.%i", HIREDIS_MAJOR, HIREDIS_MINOR, HIREDIS_PATCH);
-       }
+       INFO("rlm_redis: libhiredis version: %i.%i.%i", HIREDIS_MAJOR, HIREDIS_MINOR, HIREDIS_PATCH);
 
        inst->xlat_name = cf_section_name2(conf);
-
        if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
 
-       xlat_register(inst->xlat_name, redis_xlat, NULL, inst); /* FIXME! */
+       xlat_register(inst->xlat_name, redis_xlat, NULL, inst);
+
+       return 0;
+}
+
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+       REDIS_INST *inst = instance;
 
        inst->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, NULL);
        if (!inst->pool) {
@@ -290,21 +302,12 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
 extern module_t rlm_redis;
 module_t rlm_redis = {
-       RLM_MODULE_INIT,
-       "redis",
-       RLM_TYPE_THREAD_SAFE, /* type */
-       sizeof(REDIS_INST),     /* yuck */
-       module_config,
-       mod_instantiate, /* instantiation */
-       mod_detach, /* detach */
-       {
-               NULL, /* authentication */
-               NULL, /* authorization */
-               NULL, /* preaccounting */
-               NULL, /* accounting */
-               NULL, /* checksimul */
-               NULL, /* pre-proxy */
-               NULL, /* post-proxy */
-               NULL /* post-auth */
-       },
+       .magic          = RLM_MODULE_INIT,
+       .name           = "redis",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(REDIS_INST),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach
 };
index 27de8f6..3570b40 100644 (file)
@@ -51,7 +51,6 @@ typedef struct rlm_redis_t {
 
        int (*redis_query)(REDISSOCK **dissocket_p, REDIS_INST *inst, char const *query, REQUEST *request);
        int (*redis_finish_query)(REDISSOCK *dissocket);
-
 } rlm_redis_t;
 
 #define MAX_QUERY_LEN                  4096
index 7567a9a..11c3cf4 100644 (file)
@@ -47,11 +47,24 @@ typedef struct rlm_rediswho_t {
         *      How many session updates to keep track of per user
         */
        int trim_count;
+
+       /*
+        *      These are used only for parsing.  They aren't used at run-time.
+        */
        char const *insert;
        char const *trim;
        char const *expire;
+
 } rlm_rediswho_t;
 
+static CONF_PARSER section_config[] = {
+       { "insert", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, insert), NULL },
+       { "trim", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_rediswho_t, trim), NULL }, /* required only if trim_count > 0 */
+       { "expire", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, expire), NULL },
+
+       CONF_PARSER_TERMINATOR
+};
+
 static CONF_PARSER module_config[] = {
        { "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" },
@@ -59,11 +72,15 @@ static CONF_PARSER module_config[] = {
        { "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" },
 
-       { "insert", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, insert), NULL },
-       { "trim", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_rediswho_t, trim), NULL }, /* required only if trim_count > 0 */
-       { "expire", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, expire), NULL },
+       /*
+        *      These all smash the same variables, because we don't care about them right now.
+        *      In 3.1, we should have a way of saying "parse a set of sub-sections according to a template"
+        */
+       {  "Start", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), section_config },
+       {  "Interim-Update", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), section_config },
+       {  "Stop", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), section_config },
 
-       { NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -123,8 +140,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        inst->cs = conf;
 
-       modinst = find_module_instance(cf_section_find("modules"),
-                                      inst->redis_instance_name, true);
+       modinst = module_instantiate(cf_section_find("modules"),
+                                      inst->redis_instance_name);
        if (!modinst) {
                ERROR("rediswho: failed to find module instance \"%s\"",
                       inst->redis_instance_name);
@@ -144,24 +161,27 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 }
 
 static int mod_accounting_all(REDISSOCK **dissocket_p,
-                             rlm_rediswho_t *inst, REQUEST *request)
+                                  rlm_rediswho_t *inst, REQUEST *request,
+                                  char const *insert,
+                                  char const *trim,
+                                  char const *expire)
 {
        int result;
 
-       result = rediswho_command(inst->insert, dissocket_p, inst, request);
+       result = rediswho_command(insert, dissocket_p, inst, request);
        if (result < 0) {
                return RLM_MODULE_FAIL;
        }
 
        /* Only trim if necessary */
        if (inst->trim_count >= 0 && result > inst->trim_count) {
-               if (rediswho_command(inst->trim, dissocket_p,
+               if (rediswho_command(trim, dissocket_p,
                                     inst, request) < 0) {
                        return RLM_MODULE_FAIL;
                }
        }
 
-       if (rediswho_command(inst->expire, dissocket_p, inst, request) < 0) {
+       if (rediswho_command(expire, dissocket_p, inst, request) < 0) {
                return RLM_MODULE_FAIL;
        }
 
@@ -174,10 +194,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void * instance, REQUEST * re
        VALUE_PAIR * vp;
        DICT_VALUE *dv;
        CONF_SECTION *cs;
+       char const *insert, *trim, *expire;
        rlm_rediswho_t *inst = (rlm_rediswho_t *) instance;
        REDISSOCK *dissocket;
 
-       vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
        if (!vp) {
                RDEBUG("Could not find account status type in packet");
                return RLM_MODULE_NOOP;
@@ -198,7 +219,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void * instance, REQUEST * re
        dissocket = fr_connection_get(inst->redis_inst->pool);
        if (!dissocket) return RLM_MODULE_FAIL;
 
-       rcode = mod_accounting_all(&dissocket, inst, request);
+       insert = cf_pair_value(cf_pair_find(cs, "insert"));
+       trim = cf_pair_value(cf_pair_find(cs, "trim"));
+       expire = cf_pair_value(cf_pair_find(cs, "expire"));
+
+       rcode = mod_accounting_all(&dissocket, inst, request,
+                                       insert,
+                                       trim,
+                                       expire);
 
        if (dissocket) fr_connection_release(inst->redis_inst->pool, dissocket);
 
@@ -207,21 +235,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void * instance, REQUEST * re
 
 extern module_t rlm_rediswho;
 module_t rlm_rediswho = {
-       RLM_MODULE_INIT,
-       "rediswho",
-       RLM_TYPE_THREAD_SAFE,   /* type */
-       sizeof(rlm_rediswho_t),
-       module_config,
-       mod_instantiate,        /* instantiation */
-       NULL,                   /* detach */
-       {
-               NULL, /* authentication */
-               NULL, /* authorization */
-               NULL, /* preaccounting */
-               mod_accounting, /* accounting */
-               NULL, /* checksimul */
-               NULL, /* pre-proxy */
-               NULL, /* post-proxy */
-               NULL /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "rediswho",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_rediswho_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_ACCOUNTING]        = mod_accounting
        },
 };
index 0c6a3dc..aaba1da 100644 (file)
@@ -67,7 +67,7 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
         *      Send as many packets as necessary to different
         *      destinations.
         */
-       fr_cursor_init(&cursor, &request->config_items);
+       fr_cursor_init(&cursor, &request->config);
        while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) {
                realm = realm_find2(vp->vp_strvalue);
                if (!realm) {
@@ -98,7 +98,7 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
 #ifdef WITH_COA
                case PW_CODE_COA_REQUEST:
                case PW_CODE_DISCONNECT_REQUEST:
-                       pool = realm->acct_pool;
+                       pool = realm->coa_pool;
                        break;
 #endif
                }
@@ -146,7 +146,7 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
                         *      attributes.
                         */
                        if (*vps) {
-                               packet->vps = paircopy(packet, *vps);
+                               packet->vps = fr_pair_list_copy(packet, *vps);
                                if (!packet->vps) {
                                        rcode = RLM_MODULE_FAIL;
                                        goto done;
@@ -158,10 +158,10 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
                         *      it doesn't exist.
                         */
                        if ((code == PW_CODE_ACCESS_REQUEST) &&
-                           (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
-                           (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
+                           (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
+                           (fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
                                uint8_t *p;
-                               vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
+                               vp = radius_pair_create(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);
@@ -174,8 +174,7 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
                        }
 
                        packet->id++;
-                       talloc_free(packet->data);
-                       packet->data = NULL;
+                       TALLOC_FREE(packet->data);
                        packet->data_len = 0;
                }
 
@@ -225,6 +224,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
 }
 
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
+{
+       return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
+}
+
 static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
 {
        return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
@@ -255,29 +259,18 @@ static rlm_rcode_t CC_HINT(nonnull) mod_recv_coa(void *instance, REQUEST *reques
  */
 extern module_t rlm_replicate;
 module_t rlm_replicate = {
-       RLM_MODULE_INIT,
-       "replicate",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       0,
-       NULL,                           /* CONF_PARSER */
-       NULL,                           /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,          /* authorization */
-               mod_preaccounting,      /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "replicate",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_PREACCT]           = mod_preaccounting,
 #ifdef WITH_PROXY
-               mod_pre_proxy,          /* pre-proxy */
-               NULL,                   /* post-proxy */
-#else
-               NULL, NULL,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
 #endif
-               NULL                    /* post-auth */
 #ifdef WITH_COA
-               , mod_recv_coa,         /* coa-request */
-               NULL
+               [MOD_RECV_COA]          = mod_recv_coa
 #endif
        },
 };
index 6952dd1..0a3a6de 100644 (file)
@@ -3,9 +3,15 @@
 /* Build with JSON support from json-c */
 #undef HAVE_JSON
 
+/* json.h is at json-c/json.h relative to include dir */
+#undef HAVE_JSONMC_JSON_H
+
 /* Define to 1 if you have the `json_c_version' function. */
 #undef HAVE_JSON_C_VERSION
 
+/* json.h is at json/json.h relative to include dir */
+#undef HAVE_JSON_JSON_H
+
 /* Define to 1 if you have the `json_type_to_name' function. */
 #undef HAVE_JSON_TYPE_TO_NAME
 
index 189b7ff..04891c6 100755 (executable)
@@ -3562,9 +3562,246 @@ fi
 smart_prefix=
 
        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
+
+
+ac_safe=`echo "json-c/json.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir="/usr/local/include /opt/include"
+
+_smart_try_dir=
+_smart_include_dir=
+
+for _prefix in $smart_prefix ""; do
+  for _dir in $smart_try_dir; do
+    _smart_try_dir="${_smart_try_dir} ${_dir}/${_prefix}"
+  done
+
+  for _dir in $smart_include_dir; do
+    _smart_include_dir="${_smart_include_dir} ${_dir}/${_prefix}"
+  done
+done
+
+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/json.h in $try" >&5
+$as_echo_n "checking for json-c/json.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <json-c/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
+  for _prefix in $smart_prefix; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/json-c/json.h" >&5
+$as_echo_n "checking for ${_prefix}/json-c/json.h... " >&6; }
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <json-c/json.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem ${_prefix}/"
+                    { $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
+fi
+
+if test "x$smart_include" = "x"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json-c/json.h" >&5
+$as_echo_n "checking for json-c/json.h... " >&6; }
+
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <json-c/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
+
+  for prefix in $smart_prefix; do
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file="${_prefix}/${1}"
+
+  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\""
+
+  done
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=json-c/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; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json-c/json.h in $try" >&5
+$as_echo_n "checking for json-c/json.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <json-c/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
+
+smart_prefix=
+
+               if test "x$ac_cv_header_jsonmc_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;}
+                       fail="$fail json.h"
+               else
+
+$as_echo "#define HAVE_JSONMC_JSON_H 1" >>confdefs.h
+
+               fi
+       else
+
+$as_echo "#define HAVE_JSON_JSON_H 1" >>confdefs.h
+
        fi
 
 
index e38f4e4..958da81 100644 (file)
@@ -99,8 +99,16 @@ if test x$with_[]modname != xno; then
        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>.])
+               FR_SMART_CHECK_INCLUDE([json-c/json.h])
+               if test "x$ac_cv_header_jsonmc_json_h" != "xyes"; then
+                       have_json="no"
+                       AC_MSG_WARN([json-c headers not found. Use --with-jsonc-include-dir=<path>.])
+                       fail="$fail json.h"
+               else
+                       AC_DEFINE([HAVE_JSONMC_JSON_H],[1],[json.h is at json-c/json.h relative to include dir])
+               fi
+       else
+               AC_DEFINE([HAVE_JSON_JSON_H],[1],[json.h is at json/json.h relative to include dir])
        fi
 
        dnl ############################################################
index 0500e2a..eb68375 100644 (file)
@@ -36,6 +36,18 @@ RCSID("$Id$")
 
 #include "rest.h"
 
+/*
+ * This is a workaround to backward versions.
+ */
+#if defined(HAVE_JSON) && !defined(JSON_C_MINOR_VERSION) /* The versions less then 10, don't declare the 'JSON_C_MINOR_VERSION'*/
+int json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value);
+int json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value) {
+       *value = json_object_object_get(jso, key);
+
+       return (*value != NULL);
+}
+#endif
+
 /** Table of encoder/decoder support.
  *
  * Indexes in this table match the http_body_type_t enum, and should be
@@ -221,11 +233,11 @@ typedef struct rest_custom_data {
 /** Flags to control the conversion of JSON values to VALUE_PAIRs.
  *
  * These fields are set when parsing the expanded format for value pairs in
- * JSON, and control how json_pairmake_leaf and json_pairmake convert the JSON
+ * JSON, and control how json_pair_make_leaf and json_pair_make convert the JSON
  * value, and move the new VALUE_PAIR into an attribute list.
  *
- * @see json_pairmake
- * @see json_pairmake_leaf
+ * @see json_pair_make
+ * @see json_pair_make_leaf
  */
 typedef struct json_flags {
        int do_xlat;            //!< If true value will be expanded with xlat.
@@ -936,7 +948,7 @@ static void rest_request_init(REQUEST *request, rlm_rest_request_t *ctx, bool so
         *      Sorts pairs in place, oh well...
         */
        if (sort) {
-               pairsort(&request->packet->vps, attrtagcmp);
+               fr_pair_list_sort(&request->packet->vps, fr_pair_cmp_by_da_tag);
        }
        fr_cursor_init(&ctx->cursor, &request->packet->vps);
 }
@@ -964,8 +976,8 @@ static int rest_decode_plain(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_sectio
        /*
         *  Use rawlen to protect against overrun, and to cope with any binary data
         */
-       vp = pairmake_reply("REST-HTTP-Body", NULL, T_OP_ADD);
-       pairstrncpy(vp, raw, rawlen);
+       vp = pair_make_reply("REST-HTTP-Body", NULL, T_OP_ADD);
+       fr_pair_value_bstrncpy(vp, raw, rawlen);
 
        RDEBUG2("Adding reply:REST-HTTP-Body += \"%s\"", vp->vp_strvalue);
 
@@ -1105,7 +1117,7 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section
                        goto skip;
                }
 
-               vp = pairalloc(ctx, da);
+               vp = fr_pair_afrom_da(ctx, da);
                if (!vp) {
                        REDEBUG("Failed creating valuepair");
                        talloc_free(expanded);
@@ -1113,7 +1125,7 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section
                        goto error;
                }
 
-               ret = pairparsevalue(vp, expanded, -1);
+               ret = fr_pair_value_from_str(vp, expanded, -1);
                TALLOC_FREE(expanded);
                if (ret < 0) {
                        RWDEBUG("Incompatible value assignment, skipping");
@@ -1121,7 +1133,7 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section
                        goto skip;
                }
 
-               pairadd(vps, vp);
+               fr_pair_add(vps, vp);
 
                count++;
 
@@ -1162,7 +1174,7 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section
  * @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_pair_make_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
                                      TALLOC_CTX *ctx, REQUEST *request, DICT_ATTR const *da,
                                      json_flags_t *flags, json_object *leaf)
 {
@@ -1206,7 +1218,7 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_re
                to_parse = value;
        }
 
-       vp = pairalloc(ctx, da);
+       vp = fr_pair_afrom_da(ctx, da);
        if (!vp) {
                RWDEBUG("Failed creating valuepair for attribute \"%s\", skipping...", da->name);
                talloc_free(expanded);
@@ -1216,7 +1228,7 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_re
 
        vp->op = flags->op;
 
-       ret = pairparsevalue(vp, to_parse, -1);
+       ret = fr_pair_value_from_str(vp, to_parse, -1);
        talloc_free(expanded);
        if (ret < 0) {
                RWDEBUG("Incompatible value assignment for attribute \"%s\", skipping...", da->name);
@@ -1238,8 +1250,8 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_re
 @verbatim
 {
        "<attribute0>":{
-               do_xlat:<bool>,
-               is_json:<bool>,
+               "do_xlat":<bool>,
+               "is_json":<bool>,
                "op":"<operator>",
                "value":[<value0>,<value1>,<valueN>]
        },
@@ -1252,11 +1264,11 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_re
                }
        },
        "<attribute2>":"<value0>",
-       "<attributeN>":"[<value0>,<value1>,<valueN>]"
+       "<attributeN>":[<value0>,<value1>,<valueN>]
 }
 @endverbatim
  *
- * JSON valuepair flags (bools):
+ * JSON valuepair flags:
  *  - do_xlat  (optional) Controls xlat expansion of values. Defaults to true.
  *  - is_json  (optional) If true, any nested JSON data will be copied to the
  *                        VALUE_PAIR in string form. Defaults to true.
@@ -1278,7 +1290,7 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_re
  *           when 0 no more attributes will be processed.
  * @return number of attributes created or < 0 on error.
  */
-static int json_pairmake(rlm_rest_t *instance, rlm_rest_section_t *section,
+static int json_pair_make(rlm_rest_t *instance, rlm_rest_section_t *section,
                         REQUEST *request, json_object *object, UNUSED int level, int max)
 {
        struct lh_entry *entry;
@@ -1314,7 +1326,7 @@ static int json_pairmake(rlm_rest_t *instance, rlm_rest_section_t *section,
                        .is_json = 0
                };
 
-               value_pair_tmpl_t dst;
+               vp_tmpl_t dst;
                REQUEST *current = request;
                VALUE_PAIR **vps, *vp = NULL;
 
@@ -1365,8 +1377,7 @@ static int json_pairmake(rlm_rest_t *instance, rlm_rest_section_t *section,
                        /*
                         *  Process operator if present.
                         */
-                       tmp = json_object_object_get(value, "op");
-                       if (tmp) {
+                       if (json_object_object_get_ex(value, "op", &tmp)) {
                                flags.op = fr_str2int(fr_tokens, json_object_get_string(tmp), 0);
                                if (!flags.op) {
                                        RWDEBUG("Invalid operator value \"%s\", skipping...",
@@ -1378,31 +1389,28 @@ static int json_pairmake(rlm_rest_t *instance, rlm_rest_section_t *section,
                        /*
                         *  Process optional do_xlat bool.
                         */
-                       tmp = json_object_object_get(value, "do_xlat");
-                       if (tmp) {
+                       if (json_object_object_get_ex(value, "do_xlat", &tmp)) {
                                flags.do_xlat = json_object_get_boolean(tmp);
                        }
 
                        /*
                         *  Process optional is_json bool.
                         */
-                       tmp = json_object_object_get(value, "is_json");
-                       if (tmp) {
+                       if (json_object_object_get_ex(value, "is_json", &tmp)) {
                                flags.is_json = json_object_get_boolean(tmp);
                        }
 
                        /*
                         *  Value key must be present if were using the expanded syntax.
                         */
-                       value = json_object_object_get(value, "value");
-                       if (!value) {
+                       if (!json_object_object_get_ex(value, "value", &value)) {
                                RWDEBUG("Value key missing, skipping...");
                                continue;
                        }
                }
 
                /*
-                *  Setup pairmake / recursion loop.
+                *  Setup fr_pair_make / recursion loop.
                 */
                if (!flags.is_json && json_object_is_type(value, json_type_array)) {
                        elements = json_object_array_length(value);
@@ -1440,11 +1448,11 @@ static int json_pairmake(rlm_rest_t *instance, rlm_rest_section_t *section,
                                continue;
 
                                /*
-                               vp = json_pairmake(instance, section,
+                               vp = json_pair_make(instance, section,
                                                   request, value,
                                                   level + 1, max_attrs);*/
                        } else {
-                               vp = json_pairmake_leaf(instance, section, ctx, request,
+                               vp = json_pair_make_leaf(instance, section, ctx, request,
                                                        dst.tmpl_da, &flags, element);
                                if (!vp) continue;
                        }
@@ -1463,12 +1471,12 @@ static int json_pairmake(rlm_rest_t *instance, rlm_rest_section_t *section,
 /** Converts JSON response into VALUE_PAIRs and adds them to the request.
  *
  * Converts the raw JSON string into a json-c object tree and passes it to
- * json_pairmake. After the tree has been parsed json_object_put is called
+ * json_pair_make. After the tree has been parsed json_object_put is called
  * which decrements the reference count of the root node by one, and frees
  * the entire tree.
  *
  * @see rest_encode_json
- * @see json_pairmake
+ * @see json_pair_make
  *
  * @param[in] instance configuration data.
  * @param[in] section configuration data.
@@ -1499,7 +1507,7 @@ static int rest_decode_json(rlm_rest_t *instance, rlm_rest_section_t *section,
                return -1;
        }
 
-       ret = json_pairmake(instance, section, request, json, 0, REST_BODY_MAX_ATTRS);
+       ret = json_pair_make(instance, section, request, json, 0, REST_BODY_MAX_ATTRS);
 
        /*
         *  Decrement reference count for root object, should free entire JSON tree.
@@ -2006,7 +2014,7 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
        ctx->headers = curl_slist_append(ctx->headers, buffer);
        if (!ctx->headers) goto error_header;
 
-       fr_cursor_init(&headers, &request->config_items);
+       fr_cursor_init(&headers, &request->config);
        while (fr_cursor_next_by_num(&headers, PW_REST_HTTP_HEADER, 0, TAG_ANY)) {
                header = fr_cursor_remove(&headers);
                if (!strchr(header->vp_strvalue, ':')) {
index 65eb85d..2ac3798 100644 (file)
@@ -28,15 +28,15 @@ RCSIDH(other_h, "$Id$")
 #include <freeradius-devel/connection.h>
 #include "config.h"
 
-#ifdef HAVE_JSON_JSONH
-#define HAVE_JSON
-#endif
-
 #define CURL_NO_OLDIES 1
 #include <curl/curl.h>
 
 #ifdef HAVE_JSON
-#include <json/json.h>
+#  if defined(HAVE_JSONMC_JSON_H)
+#    include <json-c/json.h>
+#  elif defined(HAVE_JSON_JSON_H)
+#    include <json/json.h>
+#  endif
 #endif
 
 #define REST_URI_MAX_LEN               2048
@@ -153,7 +153,7 @@ typedef struct rlm_rest_t {
        struct timeval          connect_timeout_tv;     //!< Connection timeout timeval.
        long                    connect_timeout;        //!< Connection timeout ms.
 
-       fr_connection_pool_t    *conn_pool;     //!< Pointer to the connection pool.
+       fr_connection_pool_t    *pool;          //!< Pointer to the connection pool.
 
        rlm_rest_section_t      authorize;      //!< Configuration specific to authorisation.
        rlm_rest_section_t      authenticate;   //!< Configuration specific to authentication.
index c7d4f63..cc5bb5c 100644 (file)
@@ -43,8 +43,7 @@ static CONF_PARSER tls_config[] = {
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -75,15 +74,13 @@ static const CONF_PARSER section_config[] = {
 
        /* TLS Parameters */
        { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER module_config[] = {
        { "connect_uri", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_t, connect_uri), NULL },
        { "connect_timeout", FR_CONF_OFFSET(PW_TYPE_TIMEVAL, rlm_rest_t, connect_timeout_tv), "4.0" },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 static int rlm_rest_perform(rlm_rest_t *instance, rlm_rest_section_t *section, void *handle, REQUEST *request,
@@ -235,7 +232,7 @@ static ssize_t rest_xlat(void *instance, REQUEST *request,
 
        RDEBUG("Expanding URI components");
 
-       handle = fr_connection_get(inst->conn_pool);
+       handle = fr_connection_get(inst->pool);
        if (!handle) return -1;
 
        /*
@@ -336,7 +333,7 @@ error:
 finish:
        rlm_rest_cleanup(instance, &section, handle);
 
-       fr_connection_release(inst->conn_pool, handle);
+       fr_connection_release(inst->pool, handle);
 
        return outlen;
 }
@@ -359,7 +356,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
 
        if (!section->name) return RLM_MODULE_NOOP;
 
-       handle = fr_connection_get(inst->conn_pool);
+       handle = fr_connection_get(inst->pool);
        if (!handle) return RLM_MODULE_FAIL;
 
        ret = rlm_rest_perform(instance, section, handle, request, NULL, NULL);
@@ -427,7 +424,7 @@ finish:
 
        rlm_rest_cleanup(instance, section, handle);
 
-       fr_connection_release(inst->conn_pool, handle);
+       fr_connection_release(inst->pool, handle);
 
        return rcode;
 }
@@ -464,7 +461,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                return RLM_MODULE_INVALID;
        }
 
-       handle = fr_connection_get(inst->conn_pool);
+       handle = fr_connection_get(inst->pool);
        if (!handle) return RLM_MODULE_FAIL;
 
        ret = rlm_rest_perform(instance, section, handle, request, username->vp_strvalue, password->vp_strvalue);
@@ -532,7 +529,7 @@ finish:
 
        rlm_rest_cleanup(instance, section, handle);
 
-       fr_connection_release(inst->conn_pool, handle);
+       fr_connection_release(inst->pool, handle);
 
        return rcode;
 }
@@ -552,7 +549,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
 
        if (!section->name) return RLM_MODULE_NOOP;
 
-       handle = fr_connection_get(inst->conn_pool);
+       handle = fr_connection_get(inst->pool);
        if (!handle) return RLM_MODULE_FAIL;
 
        ret = rlm_rest_perform(inst, section, handle, request, NULL, NULL);
@@ -588,7 +585,7 @@ finish:
 
        rlm_rest_cleanup(inst, section, handle);
 
-       fr_connection_release(inst->conn_pool, handle);
+       fr_connection_release(inst->pool, handle);
 
        return rcode;
 }
@@ -608,7 +605,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
 
        if (!section->name) return RLM_MODULE_NOOP;
 
-       handle = fr_connection_get(inst->conn_pool);
+       handle = fr_connection_get(inst->pool);
        if (!handle) return RLM_MODULE_FAIL;
 
        ret = rlm_rest_perform(inst, section, handle, request, NULL, NULL);
@@ -644,7 +641,7 @@ finish:
 
        rlm_rest_cleanup(inst, section, handle);
 
-       fr_connection_release(inst->conn_pool, handle);
+       fr_connection_release(inst->pool, handle);
 
        return rcode;
 }
@@ -781,6 +778,24 @@ static int parse_sub_section(CONF_SECTION *parent, rlm_rest_section_t *config, r
        return 0;
 }
 
+
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
+{
+       rlm_rest_t *inst = instance;
+
+       inst->xlat_name = cf_section_name2(conf);
+       if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
+
+       /*
+        *      Register the rest xlat function
+        */
+       xlat_register(inst->xlat_name, rest_xlat, rest_uri_escape, inst);
+       xlat_register("jsonquote", jsonquote_xlat, NULL, inst);
+
+       return 0;
+}
+
+
 /*
  *     Do any per-module initialization that is separate to each
  *     configured instance of the module.  e.g. set up connections
@@ -794,32 +809,18 @@ static int parse_sub_section(CONF_SECTION *parent, rlm_rest_section_t *config, r
 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);
-       xlat_register("jsonquote", jsonquote_xlat, NULL, 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) ||
+               (parse_sub_section(conf, &inst->authorize, MOD_AUTHORIZE) < 0) ||
+               (parse_sub_section(conf, &inst->authenticate, MOD_AUTHENTICATE) < 0) ||
+               (parse_sub_section(conf, &inst->accounting, MOD_ACCOUNTING) < 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))
+/*             (parse_sub_section(conf, &inst->checksimul, MOD_SESSION) < 0) || */
+               (parse_sub_section(conf, &inst->post_auth, MOD_POST_AUTH) < 0))
        {
                return -1;
        }
@@ -833,8 +834,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        inst->connect_timeout = ((inst->connect_timeout_tv.tv_usec * 1000) +
                                 (inst->connect_timeout_tv.tv_sec / 1000));
-       inst->conn_pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, mod_conn_alive, NULL);
-       if (!inst->conn_pool) return -1;
+       inst->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, mod_conn_alive, NULL);
+       if (!inst->pool) return -1;
 
        return 0;
 }
@@ -847,7 +848,7 @@ static int mod_detach(void *instance)
 {
        rlm_rest_t *inst = instance;
 
-       fr_connection_pool_delete(inst->conn_pool);
+       fr_connection_pool_free(inst->pool);
 
        /* Free any memory used by libcurl */
        rest_cleanup();
@@ -866,21 +867,18 @@ static int mod_detach(void *instance)
  */
 extern module_t rlm_rest;
 module_t rlm_rest = {
-       RLM_MODULE_INIT,
-       "rlm_rest",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_rest_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               mod_accounting,         /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "rest",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_rest_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
index 3a8ccd9..3abc2c7 100644 (file)
@@ -88,7 +88,7 @@ typedef struct rlm_ruby_t {
 static const CONF_PARSER 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 */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -153,7 +153,7 @@ static void add_vp_tuple(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vpp, VA
 
                                        char const *s1, *s2;
 
-                                       /* pairmake() will convert and find any
+                                       /* fr_pair_make() will convert and find any
                                         * errors in the pair.
                                         */
 
@@ -165,7 +165,7 @@ static void add_vp_tuple(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vpp, VA
                                                       function_name, s1, s2);
 
                                                /* xxx Might need to support other T_OP */
-                                               vp = pairmake(ctx, vpp, s1, s2, T_OP_EQ);
+                                               vp = fr_pair_make(ctx, vpp, s1, s2, T_OP_EQ);
                                                if (vp != NULL) {
                                                        DEBUG("%s: s1, s2 OK", function_name);
                                                } else {
@@ -203,7 +203,7 @@ static rlm_rcode_t CC_HINT(nonnull (4)) do_ruby(REQUEST *request, unsigned long
        char buf[BUF_SIZE]; /* same size as vp_print buffer */
 
        VALUE_PAIR *vp;
-       VALUE rb_request, rb_result, rb_reply_items, rb_config_items, rbString1, rbString2;
+       VALUE rb_request, rb_result, rb_reply_items, rb_config, rbString1, rbString2;
 
        int n_tuple;
        DEBUG("Calling ruby function %s which has id: %lu\n", function_name, func);
@@ -275,12 +275,12 @@ static rlm_rcode_t CC_HINT(nonnull (4)) do_ruby(REQUEST *request, unsigned long
                 */
                if (request) {
                        rb_reply_items = rb_ary_entry(rb_result, 1);
-                       rb_config_items = rb_ary_entry(rb_result, 2);
+                       rb_config = rb_ary_entry(rb_result, 2);
 
                        add_vp_tuple(request->reply, request, &request->reply->vps,
                                     rb_reply_items, function_name);
-                       add_vp_tuple(request, request, &request->config_items,
-                                    rb_config_items, function_name);
+                       add_vp_tuple(request, request, &request->config,
+                                    rb_config, function_name);
                }
        } else if (FIXNUM_P(rb_result)) {
                rcode = FIX2INT(rb_result);
@@ -455,25 +455,25 @@ static int mod_detach(UNUSED void *instance)
  */
 extern module_t rlm_ruby;
 module_t rlm_ruby = {
-       RLM_MODULE_INIT,
-       "ruby",
-       RLM_TYPE_THREAD_UNSAFE, /* type, ok, let's be honest, MRI is not yet treadsafe */
-       sizeof(rlm_ruby_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               mod_preacct,            /* preaccounting */
-               mod_accounting,         /* accounting */
-               mod_checksimul,         /* checksimul */
-               mod_pre_proxy,          /* pre-proxy */
-               mod_post_proxy,         /* post-proxy */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "ruby",
+       .type           = RLM_TYPE_THREAD_UNSAFE, /* type, ok, let's be honest, MRI is not yet treadsafe */
+       .inst_size      = sizeof(rlm_ruby_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preacct,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_SESSION]           = mod_checksimul,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
+               [MOD_POST_PROXY]        = mod_post_proxy,
+               [MOD_POST_AUTH]         = mod_post_auth,
 #ifdef WITH_COA
-               , mod_recv_coa,
-               mod_send_coa
+               [MOD_RECV_COA]          = mod_recv_coa,
+               [MOD_SEND_COA]          = mod_send_coa
 #endif
        },
 };
index cba2de0..e553fb2 100755 (executable)
@@ -2864,8 +2864,8 @@ fi
 smart_prefix=
 
     if test "x$ac_cv_header_acexport_h" != "xyes"; then
-      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: securid headers not found. Use --with-securid-include-dir=<path>." >&5
-$as_echo "$as_me: WARNING: securid headers not found. Use --with-securid-include-dir=<path>." >&2;}
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: securid headers not found. Use --with-rlm-securid-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: securid headers not found. Use --with-rlm-securid-include-dir=<path>." >&2;}
       fail="$fail acexport.h"
     fi
 
@@ -3047,8 +3047,8 @@ fi
 
     if test "x$ac_cv_lib_aceclnt_SD_Init" != "xyes"
     then
-      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: securid libraries not found. Use --with-securid-lib-dir=<path>." >&5
-$as_echo "$as_me: WARNING: securid libraries not found. Use --with-securid-lib-dir=<path>." >&2;}
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: securid libraries not found. Use --with-rlm-securid-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: securid libraries not found. Use --with-rlm-securid-lib-dir=<path>." >&2;}
       fail="$fail libaceclnt"
     fi
 
index 188cc26..fb8031e 100644 (file)
@@ -67,7 +67,7 @@ if test x$with_[]modname != xno; then
     smart_try_dir="$securid_include_dir"
     FR_SMART_CHECK_INCLUDE(acexport.h)
     if test "x$ac_cv_header_acexport_h" != "xyes"; then
-      AC_MSG_WARN([securid headers not found. Use --with-securid-include-dir=<path>.])
+      AC_MSG_WARN([securid headers not found. Use --with-rlm-securid-include-dir=<path>.])
       fail="$fail acexport.h"
     fi
 
@@ -80,7 +80,7 @@ if test x$with_[]modname != xno; then
     FR_SMART_CHECK_LIB(aceclnt, SD_Init)
     if test "x$ac_cv_lib_aceclnt_SD_Init" != "xyes"
     then
-      AC_MSG_WARN([securid libraries not found. Use --with-securid-lib-dir=<path>.])
+      AC_MSG_WARN([securid libraries not found. Use --with-rlm-securid-lib-dir=<path>.])
       fail="$fail libaceclnt"
     fi
 
index 160bbbb..b6dbc4a 100644 (file)
@@ -132,7 +132,7 @@ int securid_sessionlist_add(rlm_securid_t *inst,REQUEST *request, SECURID_SESSIO
         *      Generate State, since we've been asked to add it to
         *      the list.
         */
-       state = pairmake_reply("State", session->state, T_OP_EQ);
+       state = pair_make_reply("State", session->state, T_OP_EQ);
        if (!state) return -1;
        state->vp_length = SECURID_STATE_LEN;
 
@@ -164,7 +164,7 @@ int securid_sessionlist_add(rlm_securid_t *inst,REQUEST *request, SECURID_SESSIO
        pthread_mutex_unlock(&(inst->session_mutex));
 
        if (!status) {
-               pairfree(&state);
+               fr_pair_list_free(&state);
                ERROR("rlm_securid: Failed to store session");
                return -1;
        }
@@ -192,7 +192,7 @@ SECURID_SESSION *securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request)
        /*
         *      We key the sessions off of the 'state' attribute
         */
-       state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
+       state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
        if (!state) {
                return NULL;
        }
index 780c5d3..ddb9bba 100644 (file)
@@ -44,7 +44,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 
@@ -513,11 +513,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                /* reply with Access-challenge message code (11) */
 
                /* Generate Prompt attribute */
-               vp = paircreate(request->reply, PW_PROMPT, 0);
+               vp = fr_pair_afrom_num(request->reply, PW_PROMPT, 0);
 
                rad_assert(vp != NULL);
                vp->vp_integer = 0; /* no echo */
-               pairadd(&request->reply->vps, vp);
+               fr_pair_add(&request->reply->vps, vp);
 
                /* Mark the packet as a Acceess-Challenge Packet */
                request->reply->code = PW_CODE_ACCESS_CHALLENGE;
@@ -533,7 +533,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                break;
        }
 
-       if (*buffer) pairmake_reply("Reply-Message", buffer, T_OP_EQ);
+       if (*buffer) pair_make_reply("Reply-Message", buffer, T_OP_EQ);
 
        return rcode;
 }
@@ -550,21 +550,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
  */
 extern module_t rlm_securid;
 module_t rlm_securid = {
-       RLM_MODULE_INIT,
-       "securid",
-       RLM_TYPE_HUP_SAFE,      /* type */
-       sizeof(rlm_securid_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "securid",
+       .type           = RLM_TYPE_HUP_SAFE,
+       .inst_size      = sizeof(rlm_securid_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate
        },
 };
index 8a2d923..6215b03 100644 (file)
@@ -39,8 +39,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static int _mod_conn_free(int *fdp)
@@ -208,7 +207,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
         *  Look for the 'state' attribute.
         */
 #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);
+       state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
        if (state) {
                RDEBUG("Found reply to access challenge");
 
@@ -272,8 +271,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
         *      Create the challenge, and add it to the reply.
         */
 
-       pairmake_reply("Reply-Message", inst->challenge, T_OP_EQ);
-       pairmake_reply("State", buffer, T_OP_EQ);
+       pair_make_reply("Reply-Message", inst->challenge, T_OP_EQ);
+       pair_make_reply("State", buffer, T_OP_EQ);
 
        /*
         *  Mark the packet as an Access-Challenge packet.
@@ -304,12 +303,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        /*
         *  Look for the 'state' attribute.
         */
-       state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
+       state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
        if (state != NULL) {
                DEBUG("rlm_smsotp: Found reply to access challenge (AUTZ), Adding Auth-Type '%s'",inst->authtype);
 
-               pairdelete(&request->config_items, PW_AUTH_TYPE, 0, TAG_ANY); /* delete old auth-type */
-               pairmake_config("Auth-Type", inst->authtype, T_OP_SET);
+               fr_pair_delete_by_num(&request->config, PW_AUTH_TYPE, 0, TAG_ANY); /* delete old auth-type */
+               pair_make_config("Auth-Type", inst->authtype, T_OP_SET);
        }
 
        return RLM_MODULE_OK;
@@ -327,21 +326,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
  */
 extern module_t rlm_smsotp;
 module_t rlm_smsotp = {
-       RLM_MODULE_INIT,
-       "smsotp",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_smsotp_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,  /* authorization */
-               NULL,   /* preaccounting */
-               NULL,   /* accounting */
-               NULL,   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "smsotp",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_smsotp_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
index 7977913..b0e0dfb 100644 (file)
@@ -43,24 +43,22 @@ static ssize_t soh_xlat(UNUSED void *instance, REQUEST *request, char const *fmt
        VALUE_PAIR* vp[6];
        char const *osname;
 
-       /* there will be no point unless SoH-Supported = yes
-        *
-        * FIXME: should have a #define for the attribute...
-        * SoH-Supported == 2119 in dictionary.freeradius.internal
+       /*
+        * There will be no point unless SoH-Supported = yes
         */
-       vp[0] = pairfind(request->packet->vps, 2119, 0, TAG_ANY);
+       vp[0] = fr_pair_find_by_num(request->packet->vps, PW_SOH_SUPPORTED, 0, TAG_ANY);
        if (!vp[0])
                return 0;
 
 
        if (strncasecmp(fmt, "OS", 2) == 0) {
                /* OS vendor */
-               vp[0] = pairfind(request->packet->vps, 2100, 0, TAG_ANY);
-               vp[1] = pairfind(request->packet->vps, 2101, 0, TAG_ANY);
-               vp[2] = pairfind(request->packet->vps, 2102, 0, TAG_ANY);
-               vp[3] = pairfind(request->packet->vps, 2103, 0, TAG_ANY);
-               vp[4] = pairfind(request->packet->vps, 2104, 0, TAG_ANY);
-               vp[5] = pairfind(request->packet->vps, 2105, 0, TAG_ANY);
+               vp[0] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_VENDOR,  0, TAG_ANY);
+               vp[1] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_VERSION, 0, TAG_ANY);
+               vp[2] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_RELEASE, 0, TAG_ANY);
+               vp[3] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_OS_BUILD,   0, TAG_ANY);
+               vp[4] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_SP_VERSION, 0, TAG_ANY);
+               vp[5] = fr_pair_find_by_num(request->packet->vps, PW_SOH_MS_MACHINE_SP_RELEASE, 0, TAG_ANY);
 
                if (vp[0] && vp[0]->vp_integer == VENDORPEC_MICROSOFT) {
                        if (!vp[1]) {
@@ -103,12 +101,11 @@ static const CONF_PARSER module_config[] = {
         * Do SoH over DHCP?
         */
        { "dhcp", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_soh_t, dhcp), "no" },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        char const *name;
        rlm_soh_t *inst = instance;
@@ -117,6 +114,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        if (!name) name = cf_section_name1(conf);
        inst->xlat_name = name;
        if (!inst->xlat_name) return -1;
+
        xlat_register(inst->xlat_name, soh_xlat, NULL, inst);
 
        return 0;
@@ -131,7 +129,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
 
        if (!inst->dhcp) return RLM_MODULE_NOOP;
 
-       vp = pairfind(request->packet->vps, 43, DHCP_MAGIC_VENDOR, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, 43, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                /*
                 * vendor-specific options contain
@@ -155,7 +153,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
 
                                        RDEBUG("SoH adding NAP marker to DHCP reply");
                                        /* client probe; send "NAP" in the reply */
-                                       vp = paircreate(request->reply, 43, DHCP_MAGIC_VENDOR);
+                                       vp = fr_pair_afrom_num(request->reply, 43, DHCP_MAGIC_VENDOR);
                                        vp->vp_length = 5;
                                        vp->vp_octets = p = talloc_array(vp, uint8_t, vp->vp_length);
 
@@ -165,7 +163,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                                        p[3] = 'A';
                                        p[2] = 'P';
 
-                                       pairadd(&request->reply->vps, vp);
+                                       fr_pair_add(&request->reply->vps, vp);
 
                                } else {
                                        RDEBUG("SoH decoding NAP from DHCP request");
@@ -195,7 +193,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void * instance, REQUES
        int rv;
 
        /* try to find the MS-SoH payload */
-       vp = pairfind(request->packet->vps, 55, VENDORPEC_MICROSOFT, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, 55, VENDORPEC_MICROSOFT, TAG_ANY);
        if (!vp) {
                RDEBUG("SoH radius VP not found");
                return RLM_MODULE_NOOP;
@@ -213,21 +211,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void * instance, REQUES
 
 extern module_t rlm_soh;
 module_t rlm_soh = {
-       RLM_MODULE_INIT,
-       "SoH",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_soh_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                   /* detach */
-       {
-               NULL,                   /* authenticate */
-               mod_authorize,          /* authorize */
-               NULL,                   /* pre-accounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "soh",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_soh_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
index 8ff4ab3..1aa71b9 100644 (file)
@@ -54,8 +54,7 @@ static const CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
@@ -95,7 +94,7 @@ static rlm_rcode_t sometimes_return(void *instance, RADIUS_PACKET *packet, RADIU
        /*
         *      Hash based on the given key.  Usually User-Name.
         */
-       vp = pair_find_by_da(packet->vps, inst->da, TAG_ANY);
+       vp = fr_pair_find_by_da(packet->vps, inst->da, TAG_ANY);
        if (!vp) return RLM_MODULE_NOOP;
 
        hash = fr_hash(&vp->data, vp->vp_length);
@@ -168,30 +167,25 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *instance, REQUEST *requ
 
 extern module_t rlm_sometimes;
 module_t rlm_sometimes = {
-       RLM_MODULE_INIT,
-       "sometimes",
-       RLM_TYPE_HUP_SAFE,      /* needed for radmin */
-       sizeof(rlm_sometimes_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               mod_sometimes_packet,   /* authentication */
-               mod_sometimes_packet,   /* authorization */
-               mod_sometimes_packet,   /* preaccounting */
-               mod_sometimes_packet,   /* accounting */
-               NULL,
+       .magic          = RLM_MODULE_INIT,
+       .name           = "sometimes",
+       .type           = RLM_TYPE_HUP_SAFE,    /* needed for radmin */
+       .inst_size      = sizeof(rlm_sometimes_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_sometimes_packet,
+               [MOD_AUTHORIZE]         = mod_sometimes_packet,
+               [MOD_PREACCT]           = mod_sometimes_packet,
+               [MOD_ACCOUNTING]        = mod_sometimes_packet,
 #ifdef WITH_PROXY
-               mod_pre_proxy,          /* pre-proxy */
-               mod_post_proxy,         /* post-proxy */
-#else
-               NULL, NULL,
+               [MOD_PRE_PROXY]         = mod_pre_proxy,
+               [MOD_POST_PROXY]        = mod_post_proxy,
 #endif
-               mod_sometimes_reply     /* post-auth */
+               [MOD_POST_AUTH]         = mod_sometimes_reply,
 #ifdef WITH_COA
-               ,
-               mod_sometimes_packet,   /* recv-coa */
-               mod_sometimes_reply     /* send-coa */
+               [MOD_RECV_COA]          = mod_sometimes_packet,
+               [MOD_SEND_COA]          = mod_sometimes_reply,
 #endif
        },
 };
index f363964..0eb1ce5 100644 (file)
@@ -40,8 +40,8 @@ RCSID("$Id$")
 #include "rlm_sql.h"
 
 typedef struct rlm_sql_conn {
-       SQLHANDLE hdbc;
-       SQLHANDLE henv;
+       SQLHANDLE dbc_handle;
+       SQLHANDLE env_handle;
        SQLHANDLE stmt;
 } rlm_sql_db2_conn_t;
 
@@ -49,14 +49,12 @@ static int _sql_socket_destructor(rlm_sql_db2_conn_t *conn)
 {
        DEBUG2("rlm_sql_db2: Socket destructor called, closing socket");
 
-       if (conn->hdbc) {
-               SQLDisconnect(conn->hdbc);
-               SQLFreeHandle(SQL_HANDLE_DBC, conn->hdbc);
+       if (conn->dbc_handle) {
+               SQLDisconnect(conn->dbc_handle);
+               SQLFreeHandle(SQL_HANDLE_DBC, conn->dbc_handle);
        }
 
-       if (conn->henv) {
-               SQLFreeHandle(SQL_HANDLE_ENV, conn->henv);
-       }
+       if (conn->env_handle) SQLFreeHandle(SQL_HANDLE_ENV, conn->env_handle);
 
        return RLM_SQL_OK;
 }
@@ -69,9 +67,9 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_db2_conn_t));
        talloc_set_destructor(conn, _sql_socket_destructor);
 
-       /* allocate handles */
-       SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(conn->henv));
-       SQLAllocHandle(SQL_HANDLE_DBC, conn->henv, &(conn->hdbc));
+       /* Allocate handles */
+       SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(conn->env_handle));
+       SQLAllocHandle(SQL_HANDLE_DBC, conn->env_handle, &(conn->dbc_handle));
 
        /*
         *      The db2 API doesn't qualify arguments as const even when they should be.
@@ -83,7 +81,7 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                memcpy(&login, &config->sql_login, sizeof(login));
                memcpy(&password, &config->sql_password, sizeof(password));
 
-               retval = SQLConnect(conn->hdbc,
+               retval = SQLConnect(conn->dbc_handle,
                                    server, SQL_NTS,
                                    login,  SQL_NTS,
                                    password, SQL_NTS);
@@ -106,7 +104,7 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
        conn = handle->conn;
 
        /* allocate handle for statement */
-       SQLAllocHandle(SQL_HANDLE_STMT, conn->hdbc, &(conn->stmt));
+       SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc_handle, &(conn->stmt));
 
        /* execute query */
        {
@@ -116,7 +114,7 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
                retval = SQLExecDirect(conn->stmt, db2_query, SQL_NTS);
                if(retval != SQL_SUCCESS) {
                        /* XXX Check if retval means we should return RLM_SQL_RECONNECT */
-                       ERROR("Could not execute statement \"%s\"\n", query);
+                       ERROR("Could not execute statement \"%s\"", query);
                        return RLM_SQL_ERROR;
                }
        }
@@ -139,6 +137,44 @@ static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *con
        return c;
 }
 
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_db2_conn_t *conn = handle->conn;
+
+       SQLSMALLINT     fields, len, i;
+
+       char const      **names;
+       char            field[128];
+
+       SQLNumResultCols(conn->stmt, &fields);
+       if (fields == 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_array(handle, char const *, fields));
+
+       for (i = 0; i < fields; i++) {
+               char *p;
+
+               switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
+                                       field, sizeof(field), &len, NULL)) {
+               case SQL_INVALID_HANDLE:
+               case SQL_ERROR:
+                       ERROR("Failed retrieving field name at index %i", i);
+                       talloc_free(names);
+                       return RLM_SQL_ERROR;
+
+               default:
+                       break;
+               }
+
+               MEM(p = talloc_array(names, char, (size_t)len + 1));
+               strlcpy(p, field, (size_t)len + 1);
+               names[i] = p;
+       }
+       *out = names;
+
+       return RLM_SQL_OK;
+}
+
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
 {
        int c, i;
@@ -255,6 +291,7 @@ rlm_sql_module_t rlm_sql_db2 = {
        .sql_select_query               = sql_select_query,
        .sql_num_fields                 = sql_num_fields,
        .sql_affected_rows              = sql_affected_rows,
+       .sql_fields                     = sql_fields,
        .sql_fetch_row                  = sql_fetch_row,
        .sql_free_result                = sql_free_result,
        .sql_error                      = sql_error,
index ac14fd5..7a811bb 100644 (file)
@@ -52,9 +52,7 @@ static int _sql_socket_destructor(rlm_sql_firebird_conn_t *conn)
        pthread_mutex_destroy (&conn->mut);
 #endif
 
-       for (i=0; i < conn->row_fcount; i++) {
-               free(conn->row[i]);
-       }
+       for (i = 0; i < conn->row_fcount; i++) free(conn->row[i]);
 
        free(conn->row);
        free(conn->row_sizes);
@@ -80,9 +78,7 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        talloc_set_destructor(conn, _sql_socket_destructor);
 
        res = fb_init_socket(conn);
-       if (res) {
-               return -1;
-       }
+       if (res) return RLM_SQL_ERROR;
 
        if (fb_connect(conn, config)) {
                ERROR("rlm_sql_firebird: Connection failed: %s", conn->error);
@@ -149,7 +145,7 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
 #ifdef _PTHREAD_H
                pthread_mutex_unlock(&conn->mut);
 #endif
-               return -1;
+               return RLM_SQL_ERROR;
        }
 
        if (conn->statement_type != isc_info_sql_stmt_select) {
@@ -170,14 +166,6 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
        return sql_query(handle, config, query);
 }
 
-/** Returns a result set for the query.
- *
- */
-static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
-{
-       return 0;
-}
-
 /** Returns number of columns from query.
  *
  */
@@ -194,6 +182,27 @@ static int sql_num_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
        return sql_affected_rows(handle, config);
 }
 
+/** Returns name of fields.
+ *
+ */
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_firebird_conn_t *conn = handle->conn;
+
+       int             fields, i;
+       char const      **names;
+
+       fields = conn->sqlda_out->sqld;
+       if (fields <= 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_array(handle, char const *, fields));
+
+       for (i = 0; i < fields; i++) names[i] = conn->sqlda_out->sqlvar[i].sqlname;
+       *out = names;
+
+       return RLM_SQL_OK;
+}
+
 /** Returns an individual row.
  *
  */
@@ -213,7 +222,7 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config
                if (res) {
                        ERROR("rlm_sql_firebird. Fetch problem: %s", conn->error);
 
-                       return -1;
+                       return RLM_SQL_ERROR;
                }
        } else {
                conn->statement_type=0;
@@ -299,11 +308,11 @@ rlm_sql_module_t rlm_sql_firebird = {
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
-       .sql_store_result               = sql_store_result,
        .sql_num_fields                 = sql_num_fields,
        .sql_num_rows                   = sql_num_rows,
        .sql_affected_rows              = sql_affected_rows,
        .sql_fetch_row                  = sql_fetch_row,
+       .sql_fields                     = sql_fields,
        .sql_free_result                = sql_free_result,
        .sql_error                      = sql_error,
        .sql_finish_query               = sql_finish_query,
index b90ad66..cf248d7 100644 (file)
@@ -324,6 +324,57 @@ static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *con
        return num;
 }
 
+/*************************************************************************
+ *
+ *     Function: sql_fields
+ *
+ *     Purpose:  Return name of regular result columns.
+ *
+ *************************************************************************/
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_freetds_conn_t *conn = handle->conn;
+       CS_DATAFMT datafmt;
+       int fields, i;
+       char const **names;
+
+       /* Get number of elements in row result */
+       if (ct_res_info(conn->command, CS_NUMDATA, (CS_INT *)&fields, CS_UNUSED, NULL) != CS_SUCCEED) {
+               ERROR("rlm_sql_freetds: sql_fields() Error retrieving column count");
+
+               return RLM_SQL_ERROR;
+       }
+
+       if (fields <= 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_array(handle, char const *, fields));
+
+       for (i = 0; i < fields; i++) {
+               int col = i + 1;
+               char *p;
+
+               /*
+               ** Get the column description.  ct_describe() fills the
+               ** datafmt parameter with a description of the column.
+               */
+               if (ct_describe(conn->command, col, &datafmt) != CS_SUCCEED) {
+                       ERROR("rlm_sql_freetds: sql_fields() Problems with ct_describe(), column %d", col);
+                       talloc_free(names);
+                       return RLM_SQL_ERROR;
+               }
+
+               if (datafmt.namelen > 0) {
+                       MEM(p = talloc_array(names, char, (size_t)datafmt.namelen + 1));
+                       strlcpy(p, datafmt.name, (size_t)datafmt.namelen + 1);
+                       names[i] = p;
+               }
+       }
+
+       *out = names;
+
+       return RLM_SQL_OK;
+}
+
 /** Retrieves any errors associated with the connection handle
  *
  * @note Caller will free any memory allocated in ctx.
@@ -497,14 +548,6 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
        return RLM_SQL_OK;
 }
 
-static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
-{
-       /*
-        *      Not needed for freetds, code that may have gone here iS in sql_select_query and sql_fetch_row
-        */
-       return RLM_SQL_OK;
-}
-
 static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
        rlm_sql_freetds_conn_t *conn = handle->conn;
@@ -758,9 +801,9 @@ rlm_sql_module_t rlm_sql_freetds = {
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
-       .sql_store_result               = sql_store_result,
        .sql_num_fields                 = sql_num_fields,
        .sql_num_rows                   = sql_num_rows,
+       .sql_fields                     = sql_fields,
        .sql_affected_rows              = sql_affected_rows,
        .sql_fetch_row                  = sql_fetch_row,
        .sql_free_result                = sql_free_result,
index b0930a4..12cb348 100755 (executable)
@@ -2812,7 +2812,7 @@ fi
                fail="$fail libiodbc"
        fi
 
-               smart_try_dir="$iodbc_include_dir /usr/local/iodbc/include"
+               smart_try_dir="$iodbc_include_dir /usr/include /usr/include/iodbc /usr/local/iodbc/include"
 
 
 ac_safe=`echo "isql.h" | sed 'y%./+-%__pm%'`
index dab2262..ba6304f 100644 (file)
@@ -64,7 +64,7 @@ if test x$with_[]modname != xno; then
        fi
 
        dnl Check for isql.h
-       smart_try_dir="$iodbc_include_dir /usr/local/iodbc/include"
+       smart_try_dir="$iodbc_include_dir /usr/include /usr/include/iodbc /usr/local/iodbc/include"
        FR_SMART_CHECK_INCLUDE(isql.h)
        if test "x$ac_cv_header_isql_h" != xyes; then
                fail="$fail isql.h"
index 8c52378..682b240 100644 (file)
@@ -40,7 +40,7 @@ USES_APPLE_DEPRECATED_API
 typedef struct rlm_sql_iodbc_conn {
        HENV    env_handle;
        HDBC    dbc_handle;
-       HSTMT   stmt_handle;
+       HSTMT   stmt;
        int     id;
 
        rlm_sql_row_t row;
@@ -58,18 +58,14 @@ static int _sql_socket_destructor(rlm_sql_iodbc_conn_t *conn)
 {
        DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
 
-       if (conn->stmt_handle) {
-               SQLFreeStmt(conn->stmt_handle, SQL_DROP);
-       }
+       if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP);
 
        if (conn->dbc_handle) {
                SQLDisconnect(conn->dbc_handle);
                SQLFreeConnect(conn->dbc_handle);
        }
 
-       if (conn->env_handle) {
-               SQLFreeEnv(conn->env_handle);
-       }
+       if (conn->env_handle) SQLFreeEnv(conn->env_handle);
 
        return 0;
 }
@@ -89,7 +85,7 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                ERROR("rlm_sql_iodbc: SQLAllocEnv failed");
                if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
 
-               return -1;
+               return RLM_SQL_ERROR;
        }
 
        rcode = SQLAllocConnect(conn->env_handle,
@@ -98,7 +94,7 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                ERROR("rlm_sql_iodbc: SQLAllocConnect failed");
                if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
 
-               return -1;
+               return RLM_SQL_ERROR;
        }
 
        /*
@@ -117,7 +113,7 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                ERROR("rlm_sql_iodbc: SQLConnectfailed");
                if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
 
-               return -1;
+               return RLM_SQL_ERROR;
        }
 
        return 0;
@@ -128,22 +124,22 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
        rlm_sql_iodbc_conn_t *conn = handle->conn;
        SQLRETURN rcode;
 
-       rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt_handle);
-       if (!SQL_SUCCEEDED(rcode)) return -1;
+       rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt);
+       if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
 
        if (!conn->dbc_handle) {
                ERROR("rlm_sql_iodbc: Socket not connected");
-               return -1;
+               return RLM_SQL_ERROR;
        }
 
        {
                SQLCHAR *statement;
 
                memcpy(&statement, &query, sizeof(statement));
-               rcode = SQLExecDirect(conn->stmt_handle, statement, SQL_NTS);
+               rcode = SQLExecDirect(conn->stmt, statement, SQL_NTS);
        }
 
-       if (!SQL_SUCCEEDED(rcode)) return -1;
+       if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
 
        return 0;
 }
@@ -156,9 +152,7 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
        long len = 0;
        rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-       if(sql_query(handle, config, query) < 0) {
-               return -1;
-       }
+       if (sql_query(handle, config, query) < 0) return RLM_SQL_ERROR;
 
        numfields = sql_num_fields(handle, config);
 
@@ -167,7 +161,7 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
        row[numfields] = NULL;
 
        for(i=1; i<=numfields; i++) {
-               SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
+               SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
                len++;
 
                /*
@@ -182,7 +176,7 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
                 *
                 * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
                 */
-               SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
+               SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
        }
 
        conn->row = row;
@@ -190,31 +184,53 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
        return 0;
 }
 
-static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
-{
-       return 0;
-}
-
 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
 
        SQLSMALLINT count=0;
        rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-       SQLNumResultCols(conn->stmt_handle, &count);
+       SQLNumResultCols(conn->stmt, &count);
 
        return (int)count;
 }
 
-static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
-       /*
-        * I presume this function is used to determine the number of
-        * rows in a result set *before* fetching them.  I don't think
-        * this is possible in ODBC 2.x, but I'd be happy to be proven
-        * wrong.  If you know how to do this, email me at jeff@apex.net
-        */
-       return 0;
+       rlm_sql_iodbc_conn_t *conn = handle->conn;
+
+       SQLSMALLINT     fields, len, i;
+
+       char const      **names;
+       char            field[128];
+
+       SQLNumResultCols(conn->stmt, &fields);
+       if (fields == 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_array(handle, char const *, fields));
+
+       for (i = 0; i < fields; i++) {
+               char *p;
+
+               switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
+                                       field, sizeof(field), &len, NULL)) {
+               case SQL_INVALID_HANDLE:
+               case SQL_ERROR:
+                       ERROR("Failed retrieving field name at index %i", i);
+                       talloc_free(names);
+                       return RLM_SQL_ERROR;
+
+               default:
+                       break;
+               }
+
+               MEM(p = talloc_array(names, char, (size_t)len + 1));
+               strlcpy(p, field, (size_t)len + 1);
+               names[i] = p;
+       }
+       *out = names;
+
+       return RLM_SQL_OK;
 }
 
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
@@ -224,7 +240,7 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config
 
        handle->row = NULL;
 
-       if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
+       if((rc = SQLFetch(conn->stmt)) == SQL_NO_DATA_FOUND) {
                return 0;
        }
        /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
@@ -242,7 +258,7 @@ static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        free(conn->row);
        conn->row = NULL;
 
-       SQLFreeStmt(conn->stmt_handle, SQL_DROP);
+       SQLFreeStmt(conn->stmt, SQL_DROP);
 
        return 0;
 }
@@ -270,7 +286,7 @@ static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
        rad_assert(outlen > 0);
 
        errbuff[0] = '\0';
-       SQLError(conn->env_handle, conn->dbc_handle, conn->stmt_handle,
+       SQLError(conn->env_handle, conn->dbc_handle, conn->stmt,
                 state, &errornum, errbuff, IODBC_MAX_ERROR_LEN, &length);
        if (errbuff[0] == '\0') return 0;
 
@@ -295,7 +311,7 @@ static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
        long count;
        rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-       SQLRowCount(conn->stmt_handle, &count);
+       SQLRowCount(conn->stmt, &count);
        return (int)count;
 }
 
@@ -306,10 +322,9 @@ rlm_sql_module_t rlm_sql_iodbc = {
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
-       .sql_store_result               = sql_store_result,
        .sql_num_fields                 = sql_num_fields,
-       .sql_num_rows                   = sql_num_rows,
        .sql_affected_rows              = sql_affected_rows,
+       .sql_fields                     = sql_fields,
        .sql_fetch_row                  = sql_fetch_row,
        .sql_free_result                = sql_free_result,
        .sql_error                      = sql_error,
index fb14e16..d37482f 100644 (file)
@@ -70,12 +70,14 @@ typedef struct rlm_sql_mysql_conn {
 } 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              *warnings_str;
+       char const *tls_ca_file;                //!< Path to the CA used to validate the server's certificate.
+       char const *tls_ca_path;                //!< Directory containing CAs that may be used to validate the
+                                               //!< servers certificate.
+       char const *tls_certificate_file;       //!< Public certificate we present to the server.
+       char const *tls_private_key_file;       //!< Private key for the certificate we present to the server.
+       char const *tls_cipher;
+
+       char const *warnings_str;               //!< Whether we always query the server for additional warnings.
        rlm_sql_mysql_warnings  warnings;       //!< mysql_warning_count() doesn't
                                                //!< appear to work with NDB cluster
 } rlm_sql_mysql_config_t;
@@ -90,16 +92,14 @@ static CONF_PARSER tls_config[] = {
         *      MySQL Specific TLS attributes
         */
        { "cipher", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_mysql_config_t, tls_cipher), NULL },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER driver_config[] = {
        { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
 
        { "warnings", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_mysql_config_t, warnings_str), "auto" },
-
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 /* Prototypes */
@@ -118,11 +118,7 @@ static int _sql_socket_destructor(rlm_sql_mysql_conn_t *conn)
 
 static int _mod_destructor(UNUSED rlm_sql_mysql_config_t *driver)
 {
-       mysql_instance_count--;
-
-       if (mysql_instance_count == 0) {
-                mysql_library_end();
-       }
+       if (--mysql_instance_count == 0) mysql_library_end();
 
        return 0;
 }
@@ -248,7 +244,7 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                                        config->sql_login,
                                        config->sql_password,
                                        config->sql_db,
-                                       atoi(config->sql_port),
+                                       config->sql_port,
                                        NULL,
                                        sql_flags);
        if (!conn->sock) {
@@ -436,6 +432,32 @@ static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *confi
        return 0;
 }
 
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_mysql_conn_t *conn = handle->conn;
+
+       unsigned int    fields, i;
+       MYSQL_FIELD     *field_info;
+       char const      **names;
+
+       fields = mysql_num_fields(conn->result);
+       if (fields == 0) return RLM_SQL_ERROR;
+
+       /*
+        *      https://bugs.mysql.com/bug.php?id=32318
+        *      Hints that we don't have to free field_info.
+        */
+       field_info = mysql_fetch_fields(conn->result);
+       if (!field_info) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_zero_array(handle, char const *, fields + 1));
+
+       for (i = 0; i < fields; i++) names[i] = field_info[i].name;
+       *out = names;
+
+       return RLM_SQL_OK;
+}
+
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
 {
        rlm_sql_mysql_conn_t *conn = handle->conn;
@@ -699,6 +721,7 @@ rlm_sql_module_t rlm_sql_mysql = {
        .sql_num_fields                 = sql_num_fields,
        .sql_num_rows                   = sql_num_rows,
        .sql_affected_rows              = sql_affected_rows,
+       .sql_fields                     = sql_fields,
        .sql_fetch_row                  = sql_fetch_row,
        .sql_free_result                = sql_free_result,
        .sql_error                      = sql_error,
index df103f7..1a2dce1 100644 (file)
@@ -44,11 +44,6 @@ static sql_rcode_t sql_query(UNUSED rlm_sql_handle_t * handle,
        return 0;
 }
 
-static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
-{
-       return 0;
-}
-
 static int sql_num_fields(UNUSED rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
 {
        return 0;
@@ -65,8 +60,10 @@ static int sql_num_rows(UNUSED rlm_sql_handle_t * handle, UNUSED rlm_sql_config_
        return 0;
 }
 
-static sql_rcode_t sql_fetch_row(UNUSED rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
+static sql_rcode_t sql_fetch_row(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
 {
+       handle->row = NULL;
+
        return 0;
 }
 
@@ -106,7 +103,6 @@ rlm_sql_module_t rlm_sql_null = {
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
-       .sql_store_result               = sql_store_result,
        .sql_num_fields                 = sql_num_fields,
        .sql_num_rows                   = sql_num_rows,
        .sql_fetch_row                  = sql_fetch_row,
index f875708..8214a44 100755 (executable)
@@ -2882,8 +2882,11 @@ $as_echo "$as_me: WARNING: oracle headers not found. Use --with-oracle-include-d
        lib_path="${ORACLE_HOME}/lib "
     fi
 
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Oracle version >= 12 needs -laio" >&5
+$as_echo "$as_me: WARNING: Oracle version >= 12 needs -laio" >&2;}
+
     for path in $lib_path "/usr/local/instaclient/lib" "" "/opt/lib"; do
-       for oracle_version in 11 10 9 ""; do
+       for oracle_version in 12.1 12 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; }
@@ -2893,6 +2896,7 @@ $as_echo_n "checking for OCIInitialize in nnz${oracle_version}... " >&6; }
            fi
 
            LIBS="$old_LIBS -L$path -lclntsh -lnnz${oracle_version}"
+
            cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <oci.h>
@@ -2948,7 +2952,7 @@ $as_echo "no" >&6; }
     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: 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-12]"
     fi
 
     targetname=rlm_sql_oracle
index a40aa2c..3178462 100644 (file)
@@ -87,8 +87,10 @@ if test x$with_[]modname != xno; then
        lib_path="${ORACLE_HOME}/lib "
     fi
 
+    AC_MSG_WARN([Oracle version >= 12 needs -laio])
+
     for path in $lib_path "/usr/local/instaclient/lib" "" "/opt/lib"; do
-       for oracle_version in 11 10 9 ""; do
+       for oracle_version in 12.1 12 11 10 9 ""; do
            if test "$path" != ""; then
                AC_MSG_CHECKING([for OCIInitialize in nnz${oracle_version} in $path])
            else
@@ -96,6 +98,7 @@ if test x$with_[]modname != xno; then
            fi
 
            LIBS="$old_LIBS -L$path -lclntsh -lnnz${oracle_version}"
+
            AC_TRY_LINK([#include <oci.h>
 
                static OCIEnv           *p_env;
@@ -136,7 +139,7 @@ if test x$with_[]modname != xno; then
 
     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]"]
+       fail=["$fail libclntsh libnnz[9-12]"]
     fi
 
     targetname=modname
index 5bced0d..d79fb72 100644 (file)
@@ -129,21 +129,10 @@ unknown:
 
 static int _sql_socket_destructor(rlm_sql_oracle_conn_t *conn)
 {
-       if (conn->ctx) {
-               OCILogoff(conn->ctx, conn->error);
-       }
-
-       if (conn->query) {
-               OCIHandleFree((dvoid *)conn->query, OCI_HTYPE_STMT);
-       }
-
-       if (conn->error) {
-               OCIHandleFree((dvoid *)conn->error, OCI_HTYPE_ERROR);
-       }
-
-       if (conn->env) {
-               OCIHandleFree((dvoid *)conn->env, OCI_HTYPE_ENV);
-       }
+       if (conn->ctx) OCILogoff(conn->ctx, conn->error);
+       if (conn->query) OCIHandleFree((dvoid *)conn->query, OCI_HTYPE_STMT);
+       if (conn->error) OCIHandleFree((dvoid *)conn->error, OCI_HTYPE_ERROR);
+       if (conn->env) OCIHandleFree((dvoid *)conn->env, OCI_HTYPE_ENV);
 
        return 0;
 }
@@ -213,6 +202,47 @@ static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *con
        return count;
 }
 
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+{
+       rlm_sql_oracle_conn_t *conn = handle->conn;
+       int             fields, i, status;
+       char const      **names;
+       OCIParam        *param;
+
+       fields = sql_num_fields(handle, config);
+       if (fields <= 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_array(handle, char const *, fields));
+
+       for (i = 0; i < fields; i++) {
+               OraText *pcol_name = NULL;
+               ub4 pcol_size = 0;
+
+               status = OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)&param, i + 1);
+               if (status != OCI_SUCCESS) {
+                       ERROR("rlm_sql_oracle: OCIParamGet(OCI_HTYPE_STMT) failed in sql_fields()");
+               error:
+                       talloc_free(names);
+
+                       return RLM_SQL_ERROR;
+               }
+
+               status = OCIAttrGet((dvoid **)param, OCI_DTYPE_PARAM, &pcol_name, &pcol_size,
+                                   OCI_ATTR_NAME, conn->error);
+               if (status != OCI_SUCCESS) {
+                       ERROR("rlm_sql_oracle: OCIParamGet(OCI_ATTR_NAME) failed in sql_fields()");
+
+                       goto error;
+               }
+
+               names[i] = (char const *)pcol_name;
+       }
+
+       *out = names;
+
+       return RLM_SQL_OK;
+}
+
 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
 {
        int status;
@@ -383,12 +413,6 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
        return RLM_SQL_ERROR;
 }
 
-static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle,UNUSED rlm_sql_config_t *config)
-{
-       /* Not needed for Oracle */
-       return 0;
-}
-
 static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
        rlm_sql_oracle_conn_t *conn = handle->conn;
@@ -402,7 +426,6 @@ static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *confi
 
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
 {
-
        int status;
        rlm_sql_oracle_conn_t *conn = handle->conn;
 
@@ -471,11 +494,11 @@ rlm_sql_module_t rlm_sql_oracle = {
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
-       .sql_store_result               = sql_store_result,
        .sql_num_fields                 = sql_num_fields,
        .sql_num_rows                   = sql_num_rows,
        .sql_affected_rows              = sql_affected_rows,
        .sql_fetch_row                  = sql_fetch_row,
+       .sql_fields                     = sql_fields,
        .sql_free_result                = sql_free_result,
        .sql_error                      = sql_error,
        .sql_finish_query               = sql_finish_query,
index 169d2dd..1c2034e 100644 (file)
@@ -71,8 +71,7 @@ typedef struct rlm_sql_postgres_conn {
 
 static CONF_PARSER driver_config[] = {
        { "send_application_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_postgres_config_t, send_application_name), "no" },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
@@ -101,26 +100,6 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
                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
         */
@@ -135,7 +114,63 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
 
                snprintf(application_name, sizeof(application_name),
                         "FreeRADIUS " RADIUSD_VERSION_STRING " - %s (%s)", progname, name);
-               db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
+       }
+
+       /*
+        *      Old style database name
+        *
+        *      Append options if they were set in the config
+        */
+       if (!strchr(config->sql_db, '=')) {
+               db_string = 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) {
+                       db_string = talloc_asprintf_append(db_string, " port=%i", 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);
+               }
+
+               if (driver->send_application_name) {
+                       db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
+               }
+
+       /*
+        *      New style parameter string
+        *
+        *      Only append options when not already present
+        */
+       } else {
+               db_string = talloc_typed_strdup(driver, config->sql_db);
+
+               if ((config->sql_server[0] != '\0') && !strstr(db_string, "host=")) {
+                       db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
+               }
+
+               if (config->sql_port && !strstr(db_string, "port=")) {
+                       db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
+               }
+
+               if ((config->sql_login[0] != '\0') && !strstr(db_string, "user=")) {
+                       db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
+               }
+
+               if ((config->sql_password[0] != '\0') && !strstr(db_string, "password=")) {
+                       db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
+               }
+
+               if (driver->send_application_name && !strstr(db_string, "application_name=")) {
+                       db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
+               }
        }
        driver->db_string = db_string;
 
@@ -221,13 +256,10 @@ static int _sql_socket_destructor(rlm_sql_postgres_conn_t *conn)
 {
        DEBUG2("rlm_sql_postgresql: Socket destructor called, closing socket");
 
-       if (!conn->db) {
-               return 0;
-       }
+       if (!conn->db) return 0;
 
        /* PQfinish also frees the memory used by the PGconn structure */
        PQfinish(conn->db);
-       conn->db = NULL;
 
        return 0;
 }
@@ -356,6 +388,24 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t
        return sql_query(handle, config, query);
 }
 
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_postgres_conn_t *conn = handle->conn;
+
+       int             fields, i;
+       char const      **names;
+
+       fields = PQnfields(conn->result);
+       if (fields <= 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_zero_array(handle, char const *, fields + 1));
+
+       for (i = 0; i < fields; i++) names[i] = PQfname(conn->result, i);
+       *out = names;
+
+       return RLM_SQL_OK;
+}
+
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
 
@@ -441,8 +491,8 @@ static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
        if (*p != '\0') {
                out[i].type = L_ERR;
                out[i].msg = p;
+               i++;
        }
-       i++;
 
        return i;
 }
@@ -464,6 +514,7 @@ rlm_sql_module_t rlm_sql_postgresql = {
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
        .sql_num_fields                 = sql_num_fields,
+       .sql_fields                     = sql_fields,
        .sql_fetch_row                  = sql_fetch_row,
        .sql_error                      = sql_error,
        .sql_finish_query               = sql_free_result,
index 869eb73..f0168b1 100644 (file)
@@ -55,24 +55,32 @@ typedef struct rlm_sql_sqlite_conn {
 } rlm_sql_sqlite_conn_t;
 
 typedef struct rlm_sql_sqlite_config {
-       char const *filename;
+       char const      *filename;
+       uint32_t        busy_timeout;
 } rlm_sql_sqlite_config_t;
 
 static const CONF_PARSER driver_config[] = {
        { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_sql_sqlite_config_t, filename), NULL },
-
-       {NULL, -1, 0, NULL, NULL}
+       { "busy_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sql_sqlite_config_t, busy_timeout), "200" },
+       CONF_PARSER_TERMINATOR
 };
 
-static sql_rcode_t sql_check_error(sqlite3 *db)
+/** Convert an sqlite status code to an sql_rcode_t
+ *
+ * @param status to convert.
+ * @return
+ *     - RLM_SQL_OK - If no errors found.
+ *     - RLM_SQL_ERROR - If a known, non-fatal, error occurred.
+ *     - RLM_SQL_ALT_QUERY - If a constraints violation occurred.
+ *     - RLM_SQL_RECONNECT - Anything else, we assume the connection can no longer be used.
+ */
+static sql_rcode_t sql_error_to_rcode(int status)
 {
-       int error = sqlite3_errcode(db);
-
        /*
         *      Lowest byte is error category, other byte may contain
         *      the extended error, depending on version.
         */
-       switch (error & 0xff) {
+       switch (status & 0xff) {
        /*
         *      Not errors
         */
@@ -98,20 +106,137 @@ static sql_rcode_t sql_check_error(sqlite3 *db)
         *      Errors with the handle, that probably require reinitialisation
         */
        default:
-               ERROR("rlm_sql_sqlite: Handle is unusable, error (%d): %s", error, sqlite3_errmsg(db));
                return RLM_SQL_RECONNECT;
        }
 }
 
+/** Determine if an error occurred, and what type of error it was
+ *
+ * @param db handle to extract error from (may be NULL).
+ * @param status to check (if unused, set to SQLITE_OK).
+ * @return
+ *     - RLM_SQL_OK - If no errors found.
+ *     - RLM_SQL_ERROR - If a known, non-fatal, error occurred.
+ *     - RLM_SQL_ALT_QUERY - If a constraints violation occurred.
+ *     - RLM_SQL_RECONNECT - Anything else. We assume the connection can no longer be used.
+ */
+static sql_rcode_t sql_check_error(sqlite3 *db, int status)
+{
+       int hstatus = SQLITE_OK;
+
+       if (db) {
+               hstatus = sqlite3_errcode(db);
+               switch (hstatus & 0xff) {
+               case SQLITE_OK:
+               case SQLITE_DONE:
+               case SQLITE_ROW:
+                       hstatus = SQLITE_OK;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       switch (status & 0xff) {
+       case SQLITE_OK:
+       case SQLITE_DONE:
+       case SQLITE_ROW:
+               status = SQLITE_OK;
+               break;
+
+       default:
+               break;
+       }
+
+       if (status != SQLITE_OK) return sql_error_to_rcode(status);
+       if (hstatus != SQLITE_OK) return sql_error_to_rcode(status);
+
+       return RLM_SQL_OK;
+}
+
+/** Print an error to the global debug log
+ *
+ * If status does not indicate success, write an error to the global error log.
+ *
+ * @note The error code will be appended to the fmt string in the format ": code 0x<hex> (<int>)[: <string>]".
+ *
+ * @param db handle to extract error from (may be NULL).
+ * @param status to check (if unused, set to SQLITE_OK).
+ * @param fmt to preprend.
+ * @param ... arguments to fmt.
+ */
+static void sql_print_error(sqlite3 *db, int status, char const *fmt, ...)
+       CC_HINT(format (printf, 3, 4)) CC_HINT(nonnull (3));
+static void sql_print_error(sqlite3 *db, int status, char const *fmt, ...)
+{
+       va_list ap;
+       char *p;
+       int hstatus = SQLITE_OK;
+
+       if (db) {
+               hstatus = sqlite3_errcode(db);
+               switch (hstatus & 0xff) {
+               case SQLITE_OK:
+               case SQLITE_DONE:
+               case SQLITE_ROW:
+                       hstatus = SQLITE_OK;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       switch (status & 0xff) {
+       case SQLITE_OK:
+       case SQLITE_DONE:
+       case SQLITE_ROW:
+               status = SQLITE_OK;
+               break;
+
+       default:
+               break;
+       }
+
+       /*
+        *      No errors!
+        */
+       if ((hstatus == SQLITE_OK) && (status == SQLITE_OK)) return;
+
+       /*
+        *      At least one error...
+        */
+       va_start(ap, fmt);
+       MEM(p = talloc_vasprintf(NULL, fmt, ap));
+       va_end(ap);
+
+       /*
+        *      Disagreement between handle, and function return code,
+        *      print them both.
+        */
+       if ((status != SQLITE_OK) && (status != hstatus)) {
+#ifdef HAVE_SQLITE3_ERRSTR
+               ERROR("rlm_sql_sqlite: %s: Code 0x%04x (%i): %s", p, status, status, sqlite3_errstr(status));
+#else
+               ERROR("rlm_sql_sqlite: %s: Code 0x%04x (%i)", p, status, status);
+#endif
+       }
+
+       if (hstatus != SQLITE_OK) ERROR("rlm_sql_sqlite: %s: Code 0x%04x (%i): %s",
+                                       p, hstatus, hstatus, sqlite3_errmsg(db));
+}
+
 #ifdef HAVE_SQLITE3_OPEN_V2
 static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
 {
-       ssize_t len;
-       char *buffer;
-       char *p, *q, *s;
-       int cl;
-       FILE *f;
-       struct stat finfo;
+       ssize_t         len;
+       int             statement_cnt = 0;
+       char            *buffer;
+       char            *p, *q, *s;
+       int             cl;
+       FILE            *f;
+       struct stat     finfo;
 
        int status;
        sqlite3_stmt *statement;
@@ -182,7 +307,7 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
                        if ((*p != 0x0a) && (*p != 0x0d) && (*p != '\t')) break;
                        cl = 1;
                } else {
-                       cl = fr_utf8_char((uint8_t *) p);
+                       cl = fr_utf8_char((uint8_t *) p, -1);
                        if (!cl) break;
                }
        }
@@ -200,30 +325,40 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
        while ((q = strchr(p, ';'))) {
                if (q[1] != '\n') {
                        p = q + 1;
+                       statement_cnt++;
                        continue;
                }
 
                *q = '\0';
 
 #ifdef HAVE_SQLITE3_PREPARE_V2
-               (void) sqlite3_prepare_v2(db, s, len, &statement, &z_tail);
+               status = sqlite3_prepare_v2(db, s, len, &statement, &z_tail);
 #else
-               (void) sqlite3_prepare(db, s, len, &>statement, &z_tail);
+               status = sqlite3_prepare(db, s, len, &statement, &z_tail);
 #endif
-               if (sql_check_error(db) != RLM_SQL_OK) {
+
+               if (sql_check_error(db, status) != RLM_SQL_OK) {
+                       sql_print_error(db, status, "Failed preparing statement %i", statement_cnt);
                        talloc_free(buffer);
                        return -1;
                }
 
-               (void) sqlite3_step(statement);
-               status = sql_check_error(db);
+               status = sqlite3_step(statement);
+               if (sql_check_error(db, status) != RLM_SQL_OK) {
+                       sql_print_error(db, status, "Failed executing statement %i", statement_cnt);
+                       sqlite3_finalize(statement);
+                       talloc_free(buffer);
+                       return -1;
+               }
 
-               (void) sqlite3_finalize(statement);
-               if ((status != RLM_SQL_OK) || sql_check_error(db)) {
+               status = sqlite3_finalize(statement);
+               if (sql_check_error(db, status) != RLM_SQL_OK) {
+                       sql_print_error(db, status, "Failed finalizing statement %i", statement_cnt);
                        talloc_free(buffer);
                        return -1;
                }
 
+               statement_cnt++;
                p = s = q + 1;
        }
 
@@ -269,12 +404,12 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
 
        if (cf_pair_find(conf, "bootstrap") && !exists) {
 #  ifdef HAVE_SQLITE3_OPEN_V2
-               int status;
-               int ret;
-               char const *p;
-               char *buff;
-               sqlite3 *db = NULL;
-               CONF_PAIR *cp;
+               int             status;
+               int             ret;
+               char const      *p;
+               char            *buff;
+               sqlite3         *db = NULL;
+               CONF_PAIR       *cp;
 
                INFO("rlm_sql_sqlite: Database doesn't exist, creating it and loading schema");
 
@@ -309,7 +444,7 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
                        goto unlink;
                }
 
-               if (sql_check_error(db) != RLM_SQL_OK) {
+               if (sql_check_error(db, status) != RLM_SQL_OK) {
                        (void) sqlite3_close(db);
 
                        goto unlink;
@@ -367,9 +502,7 @@ static int _sql_socket_destructor(rlm_sql_sqlite_conn_t *conn)
 
        if (conn->db) {
                status = sqlite3_close(conn->db);
-               if (status != SQLITE_OK) {
-                       WARN("rlm_sql_sqlite: Got SQLite error code (%u) when closing socket", status);
-               }
+               if (status != SQLITE_OK) WARN("rlm_sql_sqlite: Got SQLite error code (%u) when closing socket", status);
        }
 
        return 0;
@@ -408,26 +541,27 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        
        status = sqlite3_open(driver->filename, &(conn->db));
 #endif
-       if (!conn->db) {
-#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
 
+       if (!conn->db || (sql_check_error(conn->db, status) != RLM_SQL_OK)) {
+               sql_print_error(conn->db, status, "Error opening SQLite database \"%s\"", driver->filename);
+               return RLM_SQL_ERROR;
+       }
+       status = sqlite3_busy_timeout(conn->db, driver->busy_timeout);
+       if (sql_check_error(conn->db, status) != RLM_SQL_OK) {
+               sql_print_error(conn->db, status, "Error setting busy timeout");
                return RLM_SQL_ERROR;
        }
-
-       if (sql_check_error(conn->db) != RLM_SQL_OK) return RLM_SQL_ERROR;
 
        /*
         *      Enable extended return codes for extra debugging info.
         */
 #ifdef HAVE_SQLITE3_EXTENDED_RESULT_CODES
-       (void) sqlite3_extended_result_codes(conn->db, 1);
+       status = sqlite3_extended_result_codes(conn->db, 1);
+       if (sql_check_error(conn->db, status) != RLM_SQL_OK) {
+               sql_print_error(conn->db, status, "Error enabling extended result codes");
+               return RLM_SQL_ERROR;
+       }
 #endif
-       if (sql_check_error(conn->db) != RLM_SQL_OK) return RLM_SQL_ERROR;
 
 #ifdef HAVE_SQLITE3_CREATE_FUNCTION_V2
        status = sqlite3_create_function_v2(conn->db, "GREATEST", -1, SQLITE_ANY, NULL,
@@ -436,8 +570,9 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        status = sqlite3_create_function(conn->db, "GREATEST", -1, SQLITE_ANY, NULL,
                                         _sql_greatest, NULL, NULL);
 #endif
-       if (status != SQLITE_OK) {
-               ERROR("rlm_sql_sqlite: Failed registering 'GREATEST' sql function: %s", sqlite3_errmsg(conn->db));
+       if (sql_check_error(conn->db, status) != RLM_SQL_OK) {
+               sql_print_error(conn->db, status, "Failed registering 'GREATEST' sql function");
+               return RLM_SQL_ERROR;
        }
 
        return RLM_SQL_OK;
@@ -445,45 +580,43 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
 
 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
 {
-       rlm_sql_sqlite_conn_t *conn = handle->conn;
-       char const *z_tail;
+       rlm_sql_sqlite_conn_t   *conn = handle->conn;
+       char const              *z_tail;
+       int                     status;
 
 #ifdef HAVE_SQLITE3_PREPARE_V2
-       (void) sqlite3_prepare_v2(conn->db, query, strlen(query), &conn->statement, &z_tail);
+       status = 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);
+       status = sqlite3_prepare(conn->db, query, strlen(query), &conn->statement, &z_tail);
 #endif
 
        conn->col_count = 0;
 
-       return sql_check_error(conn->db);
+       return sql_check_error(conn->db, status);
 }
 
 
 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
 {
-       int status;
-       rlm_sql_sqlite_conn_t *conn = handle->conn;
-       char const *z_tail;
+
+       sql_rcode_t             rcode;
+       rlm_sql_sqlite_conn_t   *conn = handle->conn;
+       char const              *z_tail;
+       int                     status;
 
 #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);
 #endif
-       if (status != SQLITE_OK) return sql_check_error(conn->db);
-
-       (void) sqlite3_step(conn->statement);
+       rcode = sql_check_error(conn->db, status);
+       if (rcode != RLM_SQL_OK) return rcode;
 
-       return sql_check_error(conn->db);
-}
-
-static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
-{
-       return 0;
+       status = sqlite3_step(conn->statement);
+       return sql_check_error(conn->db, status);
 }
 
-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)
 {
        rlm_sql_sqlite_conn_t *conn = handle->conn;
 
@@ -505,6 +638,24 @@ static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *confi
        return 0;
 }
 
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_sqlite_conn_t *conn = handle->conn;
+
+       int             fields, i;
+       char const      **names;
+
+       fields = sqlite3_column_count(conn->statement);
+       if (fields <= 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_zero_array(handle, char const *, fields + 1));
+
+       for (i = 0; i < fields; i++) names[i] = sqlite3_column_name(conn->statement, i);
+       *out = names;
+
+       return RLM_SQL_OK;
+}
+
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
 {
        int status;
@@ -522,7 +673,7 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *con
        /*
         *      Error getting next row
         */
-       if (sql_check_error(conn->db) != RLM_SQL_OK) return RLM_SQL_ERROR;
+       if (sql_check_error(conn->db, status) != RLM_SQL_OK) return RLM_SQL_ERROR;
 
        /*
         *      No more rows to process (were done)
@@ -558,29 +709,27 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *con
                        break;
 
                case SQLITE_TEXT:
-                       {
-                               char const *p;
-                               p = (char const *) sqlite3_column_text(conn->statement, i);
+               {
+                       char const *p;
+                       p = (char const *) sqlite3_column_text(conn->statement, i);
 
-                               if (p) {
-                                       MEM(row[i] = talloc_typed_strdup(row, p));
-                               }
-                       }
+                       if (p) MEM(row[i] = talloc_typed_strdup(row, p));
+               }
                        break;
 
                case SQLITE_BLOB:
-                       {
-                               uint8_t const *p;
-                               size_t len;
+               {
+                       uint8_t const *p;
+                       size_t len;
 
-                               p = sqlite3_column_blob(conn->statement, i);
-                               if (p) {
-                                       len = sqlite3_column_bytes(conn->statement, i);
+                       p = sqlite3_column_blob(conn->statement, i);
+                       if (p) {
+                               len = sqlite3_column_bytes(conn->statement, i);
 
-                                       MEM(row[i] = talloc_zero_array(row, char, len + 1));
-                                       memcpy(row[i], p, len);
-                               }
+                               MEM(row[i] = talloc_zero_array(row, char, len + 1));
+                               memcpy(row[i], p, len);
                        }
+               }
                        break;
 
                default:
@@ -591,8 +740,7 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *con
        return 0;
 }
 
-static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle,
-                          UNUSED rlm_sql_config_t *config)
+static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
        rlm_sql_sqlite_conn_t *conn = handle->conn;
 
@@ -669,11 +817,11 @@ rlm_sql_module_t rlm_sql_sqlite = {
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
-       .sql_store_result               = sql_store_result,
        .sql_num_fields                 = sql_num_fields,
        .sql_num_rows                   = sql_num_rows,
        .sql_affected_rows              = sql_affected_rows,
        .sql_fetch_row                  = sql_fetch_row,
+       .sql_fields                     = sql_fields,
        .sql_free_result                = sql_free_result,
        .sql_error                      = sql_error,
        .sql_finish_query               = sql_finish_query,
index 75864c5..1ae086a 100755 (executable)
@@ -1843,7 +1843,7 @@ if test "${with_unixodbc_dir+set}" = set; then :
 fi
 
 
-               smart_try_dir="$unixodbc_lib_dir"
+               smart_try_dir="$unixodbc_lib_dir /usr/local/unixodbc/lib"
        ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -2812,7 +2812,7 @@ fi
                fail="$fail libodbc"
        fi
 
-               smart_try_dir="$unixodbc_include_dir"
+               smart_try_dir="$unixodbc_include_dir /usr/local/unixodbc/include"
 
 
 ac_safe=`echo "sql.h" | sed 'y%./+-%__pm%'`
index c543ed4..3545387 100644 (file)
@@ -57,14 +57,14 @@ if test x$with_[]modname != xno; then
                esac])
 
        dnl Check for SQLConnect in -lodbc
-       smart_try_dir="$unixodbc_lib_dir"
+       smart_try_dir="$unixodbc_lib_dir /usr/local/unixodbc/lib"
        FR_SMART_CHECK_LIB(odbc, SQLConnect)
        if test "x$ac_cv_lib_odbc_SQLConnect" != xyes; then
                fail="$fail libodbc"
        fi
 
        dnl Check for sql.h
-       smart_try_dir="$unixodbc_include_dir"
+       smart_try_dir="$unixodbc_include_dir /usr/local/unixodbc/include"
        FR_SMART_CHECK_INCLUDE(sql.h)
        if test "x$ac_cv_header_sql_h" != xyes; then
                fail="$fail sql.h"
index 2b2578c..c940c20 100644 (file)
@@ -30,7 +30,7 @@ RCSID("$Id$")
 typedef struct rlm_sql_unixodbc_conn {
        SQLHENV env;
        SQLHDBC dbc;
-       SQLHSTMT statement;
+       SQLHSTMT stmt;
        rlm_sql_row_t row;
        void *conn;
 } rlm_sql_unixodbc_conn_t;
@@ -49,21 +49,14 @@ static int _sql_socket_destructor(rlm_sql_unixodbc_conn_t *conn)
 {
        DEBUG2("rlm_sql_unixodbc: Socket destructor called, closing socket");
 
-       if (conn->statement) {
-               SQLFreeStmt(conn->statement, SQL_DROP);
-               conn->statement = NULL;
-       }
+       if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP);
 
        if (conn->dbc) {
                SQLDisconnect(conn->dbc);
                SQLFreeConnect(conn->dbc);
-               conn->dbc = NULL;
        }
 
-       if (conn->env) {
-               SQLFreeEnv(conn->env);
-               conn->env = NULL;
-       }
+       if (conn->env) SQLFreeEnv(conn->env);
 
        return 0;
 }
@@ -114,10 +107,10 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                return RLM_SQL_ERROR;
        }
 
-       /* 4. Allocate the statement */
-       err_handle = SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc, &conn->statement);
+       /* 4. Allocate the stmt */
+       err_handle = SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc, &conn->stmt);
        if (sql_check_error(err_handle, handle, config)) {
-               ERROR("rlm_sql_unixodbc: Can't allocate the statement");
+               ERROR("rlm_sql_unixodbc: Can't allocate the stmt");
                return RLM_SQL_ERROR;
        }
 
@@ -135,7 +128,7 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config,
                SQLCHAR *odbc_query;
 
                memcpy(&odbc_query, &query, sizeof(odbc_query));
-               err_handle = SQLExecDirect(conn->statement, odbc_query, strlen(query));
+               err_handle = SQLExecDirect(conn->stmt, odbc_query, strlen(query));
        }
        if ((state = sql_check_error(err_handle, handle, config))) {
                if(state == RLM_SQL_RECONNECT) {
@@ -168,35 +161,62 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
        conn->row = talloc_zero_array(conn, char *, colcount + 1); /* Space for pointers */
 
        for (i = 1; i <= colcount; i++) {
-               SQLColAttributes(conn->statement, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
+               SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
                conn->row[i - 1] = talloc_array(conn->row, char, ++len);
-               SQLBindCol(conn->statement, i, SQL_C_CHAR, (SQLCHAR *)conn->row[i - 1], len, NULL);
+               SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)conn->row[i - 1], len, NULL);
        }
 
        return RLM_SQL_OK;
 }
 
-static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
-{
-       /* Not used */
-       return 0;
-}
-
 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
 {
        rlm_sql_unixodbc_conn_t *conn = handle->conn;
        long err_handle;
        SQLSMALLINT num_fields = 0;
 
-       err_handle = SQLNumResultCols(conn->statement,&num_fields);
+       err_handle = SQLNumResultCols(conn->stmt,&num_fields);
        if (sql_check_error(err_handle, handle, config)) return -1;
 
        return num_fields;
 }
 
-static int sql_num_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
-       return sql_affected_rows(handle, config);
+       rlm_sql_unixodbc_conn_t *conn = handle->conn;
+
+       SQLSMALLINT     fields, len, i;
+
+       char const      **names;
+       char            field[128];
+
+       SQLNumResultCols(conn->stmt, &fields);
+       if (fields == 0) return RLM_SQL_ERROR;
+
+       MEM(names = talloc_array(handle, char const *, fields));
+
+       for (i = 0; i < fields; i++) {
+               char *p;
+
+               switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
+                                       field, sizeof(field), &len, NULL)) {
+               case SQL_INVALID_HANDLE:
+               case SQL_ERROR:
+                       ERROR("Failed retrieving field name at index %i", i);
+                       talloc_free(names);
+                       return RLM_SQL_ERROR;
+
+               default:
+                       break;
+               }
+
+               MEM(p = talloc_array(names, char, (size_t)len + 1));
+               strlcpy(p, field, (size_t)len + 1);
+               names[i] = p;
+       }
+       *out = names;
+
+       return RLM_SQL_OK;
 }
 
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
@@ -207,7 +227,7 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *con
 
        handle->row = NULL;
 
-       err_handle = SQLFetch(conn->statement);
+       err_handle = SQLFetch(conn->stmt);
        if(err_handle == SQL_NO_DATA_FOUND) {
                return 0;
        }
@@ -236,7 +256,7 @@ static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t * handle, rlm_sql_co
         *      So, this call does NOT free the statement at all, it merely
         *      resets it for the next call. This is terrible terrible naming.
         */
-       SQLFreeStmt(conn->statement, SQL_CLOSE);
+       SQLFreeStmt(conn->stmt, SQL_CLOSE);
 
        return 0;
 }
@@ -245,7 +265,7 @@ static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_con
 {
        rlm_sql_unixodbc_conn_t *conn = handle->conn;
 
-       SQLFreeStmt(conn->statement, SQL_CLOSE);
+       SQLFreeStmt(conn->stmt, SQL_CLOSE);
 
        return 0;
 }
@@ -282,7 +302,7 @@ static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
        rad_assert(outlen > 0);
 
        errbuff[0] = state[0] = '\0';
-       SQLError(conn->env, conn->dbc, conn->statement, state, &errnum,
+       SQLError(conn->env, conn->dbc, conn->stmt, state, &errnum,
                 errbuff, sizeof(errbuff), &length);
        if (errnum == 0) return 0;
 
@@ -313,7 +333,7 @@ static sql_rcode_t sql_check_error(long error_handle, rlm_sql_handle_t *handle,
 
        error[0] = state[0] = '\0';
 
-       SQLError(conn->env, conn->dbc, conn->statement, state, &errornum,
+       SQLError(conn->env, conn->dbc, conn->stmt, state, &errornum,
                 error, sizeof(error), &length);
 
        if (state[0] == '0') {
@@ -359,7 +379,7 @@ static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
        long error_handle;
        SQLLEN affected_rows;
 
-       error_handle = SQLRowCount(conn->statement, &affected_rows);
+       error_handle = SQLRowCount(conn->stmt, &affected_rows);
        if (sql_check_error(error_handle, handle, config)) return -1;
 
        return affected_rows;
@@ -373,10 +393,9 @@ rlm_sql_module_t rlm_sql_unixodbc = {
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
        .sql_select_query               = sql_select_query,
-       .sql_store_result               = sql_store_result,
        .sql_num_fields                 = sql_num_fields,
-       .sql_num_rows                   = sql_num_rows,
        .sql_affected_rows              = sql_affected_rows,
+       .sql_fields                     = sql_fields,
        .sql_fetch_row                  = sql_fetch_row,
        .sql_free_result                = sql_free_result,
        .sql_error                      = sql_error,
index 2ada64c..5b53953 100644 (file)
@@ -44,7 +44,7 @@ RCSID("$Id$")
 static const CONF_PARSER query_config[] = {
        { "query", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_MULTI, rlm_sql_config_t, accounting.query), NULL },
 
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -58,7 +58,7 @@ static const CONF_PARSER type_config[] = {
        { "interim-update", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) query_config },
        { "stop", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) query_config },
 
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER acct_config[] = {
@@ -67,7 +67,7 @@ static const CONF_PARSER acct_config[] = {
 
        { "type", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) type_config },
 
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER postauth_config[] = {
@@ -75,14 +75,13 @@ static const CONF_PARSER postauth_config[] = {
        { "logfile", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sql_config_t, postauth.logfile), NULL },
 
        { "query", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_MULTI, rlm_sql_config_t, postauth.query), NULL },
-
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER module_config[] = {
        { "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), "" },
+       { "server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_server), "" }, /* Must be zero length so drivers can determine if it was set */
+       { "port", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sql_config_t, sql_port), "0" },
        { "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" },
@@ -120,8 +119,7 @@ static const CONF_PARSER module_config[] = {
        { "accounting", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) acct_config },
 
        { "post-auth", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) postauth_config },
-
-       {NULL, -1, 0, NULL, NULL}
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -130,7 +128,7 @@ static const CONF_PARSER module_config[] = {
 static sql_fall_through_t fall_through(VALUE_PAIR *vp)
 {
        VALUE_PAIR *tmp;
-       tmp = pairfind(vp, PW_FALL_THROUGH, 0, TAG_ANY);
+       tmp = fr_pair_find_by_num(vp, PW_FALL_THROUGH, 0, TAG_ANY);
 
        return tmp ? tmp->vp_integer : FALL_THROUGH_DEFAULT;
 }
@@ -371,7 +369,7 @@ static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen,
                /*
                 *      Allow all multi-byte UTF8 characters.
                 */
-               utf8_len = fr_utf8_char((uint8_t const *) in);
+               utf8_len = fr_utf8_char((uint8_t const *) in, -1);
                if (utf8_len > 1) {
                        if (outlen <= utf8_len) break;
 
@@ -392,7 +390,7 @@ static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen,
                switch (in[0]) {
                case '\n':
                        if (outlen <= 2) break;
-                       out[0] = '\'';
+                       out[0] = '\\';
                        out[1] = 'n';
 
                        in++;
@@ -403,7 +401,7 @@ static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen,
 
                case '\r':
                        if (outlen <= 2) break;
-                       out[0] = '\'';
+                       out[0] = '\\';
                        out[1] = 'r';
 
                        in++;
@@ -414,7 +412,7 @@ static size_t sql_escape_func(UNUSED REQUEST *request, char *out, size_t outlen,
 
                case '\t':
                        if (outlen <= 2) break;
-                       out[0] = '\'';
+                       out[0] = '\\';
                        out[1] = 't';
 
                        in++;
@@ -495,13 +493,13 @@ int sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username)
                return -1;
        }
 
-       vp = pairalloc(request->packet, inst->sql_user);
+       vp = fr_pair_afrom_da(request->packet, inst->sql_user);
        if (!vp) {
                talloc_free(expanded);
                return -1;
        }
 
-       pairstrsteal(vp, expanded);
+       fr_pair_value_strsteal(vp, expanded);
        RDEBUG2("SQL-User-Name set to '%s'", vp->vp_strvalue);
        vp->op = T_OP_SET;
        radius_pairmove(request, &request->packet->vps, vp, false);     /* needs to be pair move else op is not respected */
@@ -512,7 +510,7 @@ int sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username)
 /*
  *     Do a set/unset user, so it's a bit clearer what's going on.
  */
-#define sql_unset_user(_i, _r) pairdelete(&_r->packet->vps, _i->sql_user->attr, _i->sql_user->vendor, TAG_ANY)
+#define sql_unset_user(_i, _r) fr_pair_delete_by_num(&_r->packet->vps, _i->sql_user->attr, _i->sql_user->vendor, TAG_ANY)
 
 static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t **handle, REQUEST *request,
                             rlm_sql_grouplist_t **phead)
@@ -572,15 +570,26 @@ static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t **handle, REQUEST
  * The group membership query should only return one element which is the username. The returned
  * 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) CC_HINT(nonnull (1, 2, 4));
 
-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)
+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)
 {
        rlm_sql_handle_t *handle;
        rlm_sql_t *inst = instance;
        rlm_sql_grouplist_t *head, *entry;
 
+       /*
+        *      No group queries, don't do group comparisons.
+        */
+       if (!inst->config->groupmemb_query) {
+               RWARN("Cannot do group comparison when group_membership_query is not set");
+               return 1;
+       }
+
        RDEBUG("sql_groupcmp");
 
        if (check->vp_length == 0){
@@ -642,6 +651,19 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
 
        rad_assert(request->packet != NULL);
 
+       if (!inst->config->groupmemb_query) {
+               RWARN("Cannot do check groups when group_membership_query is not set");
+
+       do_nothing:
+               *do_fall_through = FALL_THROUGH_DEFAULT;
+
+               /*
+                *      Didn't add group attributes or allocate
+                *      memory, so don't do anything else.
+                */
+               return RLM_MODULE_NOTFOUND;
+       }
+
        /*
         *      Get the list of groups this user is a member of
         */
@@ -653,10 +675,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
        }
        if (rows == 0) {
                RDEBUG2("User not found in any groups");
-               rcode = RLM_MODULE_NOTFOUND;
-               *do_fall_through = FALL_THROUGH_DEFAULT;
-
-               goto finish;
+               goto do_nothing;
        }
        rad_assert(head);
 
@@ -666,9 +685,9 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
         *      Add the Sql-Group attribute to the request list so we know
         *      which group we're retrieving attributes for
         */
-       sql_group = pairmake_packet("Sql-Group", NULL, T_OP_EQ);
+       sql_group = pair_make_request(inst->group_da->name, NULL, T_OP_EQ);
        if (!sql_group) {
-               REDEBUG("Error creating Sql-Group attribute");
+               REDEBUG("Error creating %s attribute", inst->group_da->name);
                rcode = RLM_MODULE_FAIL;
                goto finish;
        }
@@ -677,7 +696,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
        do {
        next:
                rad_assert(entry != NULL);
-               pairstrcpy(sql_group, entry->name);
+               fr_pair_value_strcpy(sql_group, entry->name);
 
                if (inst->config->authorize_group_check_query) {
                        vp_cursor_t cursor;
@@ -707,9 +726,11 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
                         */
                        if ((rows > 0) &&
                            (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0)) {
-                               pairfree(&check_tmp);
+                               fr_pair_list_free(&check_tmp);
                                entry = entry->next;
 
+                               if (!entry) break;
+
                                goto next;      /* != continue */
                        }
 
@@ -726,7 +747,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
                                rdebug_pair(L_DBG_LVL_2, request, vp, NULL);
                        }
                        REXDENT();
-                       radius_pairmove(request, &request->config_items, check_tmp, true);
+                       radius_pairmove(request, &request->config, check_tmp, true);
                        check_tmp = NULL;
                }
 
@@ -771,7 +792,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
 
 finish:
        talloc_free(head);
-       pairdelete(&request->packet->vps, PW_SQL_GROUP, 0, TAG_ANY);
+       fr_pair_delete_by_num(&request->packet->vps, inst->group_da->attr, 0, TAG_ANY);
 
        return rcode;
 }
@@ -781,7 +802,7 @@ static int mod_detach(void *instance)
 {
        rlm_sql_t *inst = instance;
 
-       if (inst->pool) fr_connection_pool_delete(inst->pool);
+       if (inst->pool) fr_connection_pool_free(inst->pool);
 
        /*
         *  We need to explicitly free all children, so if the driver
@@ -802,7 +823,7 @@ static int mod_detach(void *instance)
        return 0;
 }
 
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
        rlm_sql_t *inst = instance;
 
@@ -813,44 +834,74 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        inst->cs = conf;
 
        inst->name = cf_section_name2(conf);
-       if (!inst->name) {
-               inst->name = cf_section_name1(conf);
-       } else {
-               char *group_name;
-               DICT_ATTR const *da;
-               ATTR_FLAGS flags;
+       if (!inst->name) inst->name = cf_section_name1(conf);
 
-               /*
-                *      Allocate room for <instance>-SQL-Group
-                */
-               group_name = talloc_typed_asprintf(inst, "%s-SQL-Group", inst->name);
-               DEBUG("rlm_sql (%s): Creating new attribute %s",
-                     inst->name, group_name);
-
-               memset(&flags, 0, sizeof(flags));
-               if (dict_addattr(group_name, -1, 0, PW_TYPE_STRING, flags) < 0) {
-                       ERROR("rlm_sql (%s): Failed to create "
-                              "attribute %s: %s", inst->name, group_name,
-                              fr_strerror());
-                       return -1;
-               }
+       /*
+        *      Load the appropriate driver for our database.
+        *
+        *      We need this to check if the sql_fields callback is provided.
+        */
+       inst->handle = lt_dlopenext(inst->config->sql_driver_name);
+       if (!inst->handle) {
+               ERROR("Could not link driver %s: %s", inst->config->sql_driver_name, fr_strerror());
+               ERROR("Make sure it (and all its dependent libraries!) are in the search path of your system's ld");
+               return -1;
+       }
 
-               da = dict_attrbyname(group_name);
-               if (!da) {
-                       ERROR("rlm_sql (%s): Failed to create "
-                              "attribute %s", inst->name, group_name);
-                       return -1;
+       inst->module = (rlm_sql_module_t *) dlsym(inst->handle,  inst->config->sql_driver_name);
+       if (!inst->module) {
+               ERROR("Could not link symbol %s: %s", inst->config->sql_driver_name, dlerror());
+               return -1;
+       }
+
+       INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked", inst->name,
+            inst->config->sql_driver_name, inst->module->name);
+
+       if (inst->config->groupmemb_query) {
+               if (cf_section_name2(conf)) {
+                       char buffer[256];
+
+                       snprintf(buffer, sizeof(buffer), "%s-SQL-Group", inst->name);
+
+                       if (paircompare_register_byname(buffer, dict_attrbyvalue(PW_USER_NAME, 0),
+                                                       false, sql_groupcmp, inst) < 0) {
+                               ERROR("Error registering group comparison: %s", fr_strerror());
+                               return -1;
+                       }
+
+                       inst->group_da = dict_attrbyname(buffer);
+
+                       /*
+                        *      We're the default instance
+                        */
+               } else {
+                       if (paircompare_register_byname("SQL-Group", dict_attrbyvalue(PW_USER_NAME, 0),
+                                                       false, sql_groupcmp, inst) < 0) {
+                               ERROR("Error registering group comparison: %s", fr_strerror());
+                               return -1;
+                       }
+
+                       inst->group_da = dict_attrbyname("SQL-Group");
                }
 
-               if (inst->config->groupmemb_query) {
-                       DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s",
-                             inst->name, group_name);
-                       paircompare_register(da, dict_attrbyvalue(PW_USER_NAME, 0),
-                                            false, sql_groupcmp, inst);
+               if (!inst->group_da) {
+                       ERROR("Failed resolving group attribute");
+                       return -1;
                }
        }
 
-       rad_assert(inst->name);
+       /*
+        *      Register the SQL xlat function
+        */
+       xlat_register(inst->name, sql_xlat, sql_escape_func, inst);
+
+       return 0;
+}
+
+
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+       rlm_sql_t *inst = instance;
 
        /*
         *      Complain if the strings exist, but are empty.
@@ -892,19 +943,13 @@ do { \
                        WARN("rlm_sql (%s): Ignoring authorize_group_check_query as group_membership_query "
                             "is not configured", inst->name);
                }
-       } else {
-               if (!inst->config->authorize_group_check_query) {
-                       ERROR("rlm_sql (%s): authorize_group_check_query must be configured as group_membership_query "
-                             "is configured", inst->name);
-                       return -1;
-               }
 
-               if (!inst->config->authorize_group_reply_query) {
-                       ERROR("rlm_sql (%s): authorize_group_reply_query must be configured as group_membership_query "
-                             "is configured", inst->name);
-                       return -1;
+               if (!inst->config->read_groups) {
+                       WARN("rlm_sql (%s): Ignoring read_groups as group_membership_query "
+                            "is not configured", inst->name);
+                       inst->config->read_groups = false;
                }
-       }
+       } /* allow the group check / reply queries to be NULL */
 
        /*
         *      This will always exist, as cf_section_parse_init()
@@ -937,28 +982,6 @@ do { \
        inst->sql_select_query          = rlm_sql_select_query;
        inst->sql_fetch_row             = rlm_sql_fetch_row;
 
-       /*
-        *      Register the SQL xlat function
-        */
-       xlat_register(inst->name, sql_xlat, sql_escape_func, inst);
-
-       /*
-        *      Load the appropriate driver for our database
-        */
-       inst->handle = lt_dlopenext(inst->config->sql_driver_name);
-       if (!inst->handle) {
-               ERROR("Could not link driver %s: %s", 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");
-               return -1;
-       }
-
-       inst->module = (rlm_sql_module_t *) dlsym(inst->handle,
-                                                 inst->config->sql_driver_name);
-       if (!inst->module) {
-               ERROR("Could not link symbol %s: %s", inst->config->sql_driver_name, dlerror());
-               return -1;
-       }
-
        if (inst->module->mod_instantiate) {
                CONF_SECTION *cs;
                char const *name;
@@ -986,15 +1009,12 @@ do { \
                }
        }
 
-       inst->ef = exfile_init(inst, 64, 30);
+       inst->ef = exfile_init(inst, 64, 30, true);
        if (!inst->ef) {
                cf_log_err_cs(conf, "Failed creating log file context");
                return -1;
        }
 
-       INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked", inst->name,
-            inst->config->sql_driver_name, inst->module->name);
-
        /*
         *      Initialise the connection pool for this instance
         */
@@ -1003,11 +1023,6 @@ do { \
        inst->pool = fr_connection_pool_module_init(inst->cs, inst, mod_conn_create, NULL, NULL);
        if (!inst->pool) return -1;
 
-       if (inst->config->groupmemb_query) {
-               paircompare_register(dict_attrbyvalue(PW_SQL_GROUP, 0),
-                               dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst);
-       }
-
        if (inst->config->do_clients) {
                if (generate_sql_clients(inst) == -1){
                        ERROR("Failed to load clients from SQL");
@@ -1018,8 +1033,8 @@ do { \
        return RLM_MODULE_OK;
 }
 
-
-static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t mod_authorize(void *instance, REQUEST *request) CC_HINT(nonnull);
+static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
 {
        rlm_rcode_t rcode = RLM_MODULE_NOOP;
 
@@ -1097,7 +1112,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                RDEBUG2("User found in radcheck table");
                user_found = true;
                if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) {
-                       pairfree(&check_tmp);
+                       fr_pair_list_free(&check_tmp);
                        check_tmp = NULL;
                        goto skipreply;
                }
@@ -1112,7 +1127,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                        rdebug_pair(2, request, vp, NULL);
                }
                REXDENT();
-               radius_pairmove(request, &request->config_items, check_tmp, true);
+               radius_pairmove(request, &request->config, check_tmp, true);
 
                rcode = RLM_MODULE_OK;
                check_tmp = NULL;
@@ -1201,7 +1216,7 @@ skipreply:
                 *  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);
+               user_profile = fr_pair_find_by_num(request->config, PW_USER_PROFILE, 0, TAG_ANY);
 
                char const *profile = user_profile ?
                                      user_profile->vp_strvalue :
@@ -1260,8 +1275,8 @@ release:
        return rcode;
 
 error:
-       pairfree(&check_tmp);
-       pairfree(&reply_tmp);
+       fr_pair_list_free(&check_tmp);
+       fr_pair_list_free(&reply_tmp);
        sql_unset_user(inst, request);
 
        fr_connection_release(inst->pool, handle);
@@ -1308,16 +1323,20 @@ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t
                goto finish;
        }
 
+       /*
+        *      If we can't find a matching config item we do
+        *      nothing so return RLM_MODULE_NOOP.
+        */
        item = cf_reference_item(NULL, section->cs, path);
        if (!item) {
-               rcode = RLM_MODULE_FAIL;
+               RWDEBUG("No such configuration item %s", path);
+               rcode = RLM_MODULE_NOOP;
 
                goto finish;
        }
-
        if (cf_item_is_section(item)){
-               REDEBUG("Sections are not supported as references");
-               rcode = RLM_MODULE_FAIL;
+               RWDEBUG("Sections are not supported as references");
+               rcode = RLM_MODULE_NOOP;
 
                goto finish;
        }
@@ -1442,7 +1461,9 @@ finish:
 /*
  *     Accounting: Insert or update session data in our sql table
  */
-static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * request) {
+static rlm_rcode_t mod_accounting(void *instance, REQUEST *request) CC_HINT(nonnull);
+static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+{
        rlm_sql_t *inst = instance;
 
        if (inst->config->accounting.reference_cp) {
@@ -1463,8 +1484,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * req
  *     max. number of logins, do a second pass and validate all
  *     logins by querying the terminal server (using eg. SNMP).
  */
-
-static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST * request) {
+static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) CC_HINT(nonnull);
+static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request)
+{
        rlm_rcode_t             rcode = RLM_MODULE_OK;
        rlm_sql_handle_t        *handle = NULL;
        rlm_sql_t               *inst = instance;
@@ -1557,11 +1579,11 @@ static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST * req
         */
        request->simul_count = 0;
 
-       if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
                ipno = vp->vp_ipaddr;
        }
 
-       if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
                call_num = vp->vp_strvalue;
        }
 
@@ -1663,7 +1685,9 @@ release:
 /*
  *     Postauth: Write a record of the authentication attempt
  */
-static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * request) {
+static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull);
+static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+{
        rlm_sql_t *inst = instance;
 
        if (inst->config->postauth.reference_cp) {
@@ -1681,29 +1705,22 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * requ
 /* globally exported name */
 extern module_t rlm_sql;
 module_t rlm_sql = {
-       RLM_MODULE_INIT,
-       "SQL",
-       RLM_TYPE_THREAD_SAFE,   /* type: reserved */
-       sizeof(rlm_sql_t),
-       module_config,
-       mod_instantiate,        /* instantiation */
-       mod_detach,             /* detach */
-       {
-               NULL,           /* authentication */
-               mod_authorize,  /* authorization */
-               NULL,           /* preaccounting */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "sql",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_sql_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
 #ifdef WITH_ACCOUNTING
-               mod_accounting, /* accounting */
-#else
-               NULL,
+               [MOD_ACCOUNTING]        = mod_accounting,
 #endif
 #ifdef WITH_SESSION_MGMT
-               mod_checksimul, /* checksimul */
-#else
-               NULL,
+               [MOD_SESSION]           = mod_checksimul,
 #endif
-               NULL,           /* pre-proxy */
-               NULL,           /* post-proxy */
-               mod_post_auth   /* post-auth */
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
index b9bdc14..bbf5a79 100644 (file)
@@ -83,7 +83,7 @@ typedef struct sql_acct_section {
 typedef struct sql_config {
        char const              *sql_driver_name;               //!< SQL driver module name e.g. rlm_sql_sqlite.
        char const              *sql_server;                    //!< Server to connect to.
-       char const              *sql_port;                      //!< Port to connect to.
+       uint32_t                sql_port;                       //!< Port to connect to.
        char const              *sql_login;                     //!< Login credentials to use.
        char const              *sql_password;                  //!< Login password to use.
        char const              *sql_db;                        //!< Database to run queries against.
@@ -165,6 +165,29 @@ extern const FR_NAME_NUMBER sql_rcode_table[];
 #define RLM_SQL_RCODE_FLAGS_ALT_QUERY  1                       //!< Can distinguish between other errors and those
                                                                //!< resulting from a unique key violation.
 
+/** Retrieve errors from the last query operation
+ *
+ * @note Buffers allocated in the context provided will be automatically freed. The driver
+ *     should not free these buffers explicitly.
+ * @note If the driver uses its own buffers to aggregate messages, they should be cleared
+ *     on sql_query_finish, and after each call to sql_error, to prevent the same messages
+ *     being printed multiple times.
+ *
+ * @param[in,out] ctx to allocate any buffers required. If static buffers are provided by the
+ *     driver they need not be strduped, just write the pointer to those buffers to the .msg
+ *     field of a sql_log_entry_t element.
+ * @param[out] out a pre-allocated array of log entries to fill. Need not be NULL terminated.
+ * @param[in] outlen Number of log entries available for populating. Do not write to index
+ *     out[outlen] or higher.
+ * @param[in] handle to retrieve errors from.
+ * @param[in] config of the SQL instance.
+ * @return
+ *     0  - If no error messages are available.
+ *     >0 - Number of log entries
+ */
+typedef size_t (*sql_error_t)(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, rlm_sql_handle_t *handle,
+                             rlm_sql_config_t *config);
+
 typedef struct rlm_sql_module_t {
        char const      *name;
        int             flags;
@@ -184,8 +207,7 @@ typedef struct rlm_sql_module_t {
        sql_rcode_t (*sql_fields)(char const **out[], rlm_sql_handle_t *handle, rlm_sql_config_t *config);
        sql_rcode_t (*sql_free_result)(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
 
-       size_t (*sql_error)(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
-                           rlm_sql_handle_t *handle, rlm_sql_config_t *config);
+       sql_error_t     sql_error;                              //!< Get any errors from the previous query.
 
        sql_rcode_t (*sql_finish_query)(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
        sql_rcode_t (*sql_finish_select_query)(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
@@ -201,8 +223,8 @@ struct sql_inst {
                                                        //!< dictionary attribute.
        exfile_t                *ef;
 
-       void *handle;
-       rlm_sql_module_t *module;
+       void                    *handle;
+       rlm_sql_module_t        *module;
 
        int (*sql_set_user)(rlm_sql_t *inst, REQUEST *request, char const *username);
        size_t (*sql_escape_func)(REQUEST *, char *out, size_t outlen, char const *in, void *arg);
@@ -211,6 +233,7 @@ struct sql_inst {
        sql_rcode_t (*sql_fetch_row)(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle);
 
        char const              *name;                  //!< Module instance name.
+       DICT_ATTR const         *group_da;
 };
 
 typedef struct sql_grouplist {
@@ -219,10 +242,9 @@ typedef struct sql_grouplist {
 } rlm_sql_grouplist_t;
 
 void           *mod_conn_create(TALLOC_CTX *ctx, void *instance);
-int            sql_userparse(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **first_pair, rlm_sql_row_t row);
+int            sql_fr_pair_list_afrom_str(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **first_pair, rlm_sql_row_t row);
 int            sql_read_realms(rlm_sql_handle_t *handle);
 int            sql_getvpdata(TALLOC_CTX *ctx, rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle, 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);
index 0d362d5..0451002 100644 (file)
@@ -120,12 +120,12 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
 
 /*************************************************************************
  *
- *     Function: sql_userparse
+ *     Function: sql_fr_pair_list_afrom_str
  *
  *     Purpose: Read entries from the database and fill VALUE_PAIR structures
  *
  *************************************************************************/
-int sql_userparse(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **head, rlm_sql_row_t row)
+int sql_fr_pair_list_afrom_str(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **head, rlm_sql_row_t row)
 {
        VALUE_PAIR *vp;
        char const *ptr, *value;
@@ -204,21 +204,21 @@ int sql_userparse(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **head, rlm_sql_
        /*
         *      Create the pair
         */
-       vp = pairmake(ctx, NULL, row[2], NULL, operator);
+       vp = fr_pair_make(ctx, NULL, row[2], NULL, operator);
        if (!vp) {
                REDEBUG("Failed to create the pair: %s", fr_strerror());
                return -1;
        }
 
        if (do_xlat) {
-               if (pairmark_xlat(vp, value) < 0) {
+               if (fr_pair_mark_xlat(vp, value) < 0) {
                        REDEBUG("Error marking pair for xlat");
 
                        talloc_free(vp);
                        return -1;
                }
        } else {
-               if (pairparsevalue(vp, value, -1) < 0) {
+               if (fr_pair_value_from_str(vp, value, -1) < 0) {
                        REDEBUG("Error parsing value: %s", fr_strerror());
 
                        talloc_free(vp);
@@ -229,7 +229,7 @@ int sql_userparse(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **head, rlm_sql_
        /*
         *      Add the pair into the packet
         */
-       pairadd(head, vp);
+       fr_pair_add(head, vp);
        return 0;
 }
 
@@ -256,7 +256,7 @@ sql_rcode_t rlm_sql_fetch_row(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_
         */
        ret = (inst->module->sql_fetch_row)(*handle, inst->config);
        if (ret < 0) {
-               ROPTIONAL(RERROR, ERROR, "Error fetching row");
+               MOD_ROPTIONAL(RERROR, ERROR, "Error fetching row");
 
                rlm_sql_print_error(inst, request, *handle, false);
        }
@@ -282,7 +282,7 @@ void rlm_sql_print_error(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t *ha
 
        num = (inst->module->sql_error)(handle->log_ctx, log, (sizeof(log) / sizeof(*log)), handle, inst->config);
        if (num == 0) {
-               ROPTIONAL(RERROR, ERROR, "Unknown error");
+               MOD_ROPTIONAL(RERROR, ERROR, "Unknown error");
                return;
        }
 
@@ -293,21 +293,21 @@ void rlm_sql_print_error(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t *ha
 
                switch (log[i].type) {
                case L_ERR:
-                       ROPTIONAL(RERROR, ERROR, "%s: %s", driver, log[i].msg);
+                       MOD_ROPTIONAL(RERROR, ERROR, "%s: %s", driver, log[i].msg);
                        break;
 
                case L_WARN:
-                       ROPTIONAL(RWARN, WARN, "%s: %s", driver, log[i].msg);
+                       MOD_ROPTIONAL(RWARN, WARN, "%s: %s", driver, log[i].msg);
                        break;
 
                case L_INFO:
-                       ROPTIONAL(RINFO, INFO, "%s: %s", driver, log[i].msg);
+                       MOD_ROPTIONAL(RINFO, INFO, "%s: %s", driver, log[i].msg);
                        break;
 
                case L_DBG:
                default:
                debug:
-                       ROPTIONAL(RDEBUG, DEBUG, "%s: %s", driver, log[i].msg);
+                       MOD_ROPTIONAL(RDEBUG, DEBUG, "%s: %s", driver, log[i].msg);
                        break;
                }
        }
@@ -346,14 +346,14 @@ sql_rcode_t rlm_sql_query(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **
        /*
         *  inst->pool may be NULL is this function is called by mod_conn_create.
         */
-       count = inst->pool ? fr_connection_get_num(inst->pool) : 0;
+       count = inst->pool ? fr_connection_pool_get_num(inst->pool) : 0;
 
        /*
         *  Here we try with each of the existing connections, then try to create
         *  a new connection, then give up.
         */
        for (i = 0; i < (count + 1); i++) {
-               ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query);
+               MOD_ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query);
 
                ret = (inst->module->sql_query)(*handle, inst->config, query);
                switch (ret) {
@@ -410,7 +410,7 @@ sql_rcode_t rlm_sql_query(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **
                return ret;
        }
 
-       ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
+       MOD_ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
 
        return RLM_SQL_ERROR;
 }
@@ -446,13 +446,13 @@ sql_rcode_t rlm_sql_select_query(rlm_sql_t *inst, REQUEST *request, rlm_sql_hand
        /*
         *  inst->pool may be NULL is this function is called by mod_conn_create.
         */
-       count = inst->pool ? fr_connection_get_num(inst->pool) : 0;
+       count = inst->pool ? fr_connection_pool_get_num(inst->pool) : 0;
 
        /*
         *  For sanity, for when no connections are viable, and we can't make a new one
         */
        for (i = 0; i < (count + 1); i++) {
-               ROPTIONAL(RDEBUG2, DEBUG2, "Executing select query: %s", query);
+               MOD_ROPTIONAL(RDEBUG2, DEBUG2, "Executing select query: %s", query);
 
                ret = (inst->module->sql_select_query)(*handle, inst->config, query);
                switch (ret) {
@@ -481,7 +481,7 @@ sql_rcode_t rlm_sql_select_query(rlm_sql_t *inst, REQUEST *request, rlm_sql_hand
                return ret;
        }
 
-       ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
+       MOD_ROPTIONAL(RERROR, ERROR, "Hit reconnection limit");
 
        return RLM_SQL_ERROR;
 }
@@ -509,7 +509,7 @@ int sql_getvpdata(TALLOC_CTX *ctx, rlm_sql_t *inst, REQUEST *request, rlm_sql_ha
        while (rlm_sql_fetch_row(inst, request, handle) == 0) {
                row = (*handle)->row;
                if (!row) break;
-               if (sql_userparse(ctx, request, pair, row) != 0) {
+               if (sql_fr_pair_list_afrom_str(ctx, request, pair, row) != 0) {
                        REDEBUG("Error parsing user data from database result");
 
                        (inst->module->sql_finish_select_query)(*handle, inst->config);
index ecb3331..063bbed 100644 (file)
@@ -30,7 +30,7 @@ RCSID("$Id$")
 
 #include <ctype.h>
 
-#define MAX_QUERY_LEN 1024
+#define MAX_QUERY_LEN 2048
 
 /*
  *     Note: When your counter spans more than 1 period (ie 3 months
@@ -100,8 +100,7 @@ static const CONF_PARSER module_config[] = {
 
        { "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 }
+       CONF_PARSER_TERMINATOR
 };
 
 static int find_next_reset(rlm_sqlcounter_t *inst, time_t timeval)
@@ -251,43 +250,80 @@ 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 freespace;
        char const *p;
        char *q;
        char tmpdt[40]; /* For temporary storing of dates */
 
        q = out;
-       for (p = fmt; *p ; p++) {
+       p = fmt;
+       while (*p) {
                /* Calculate freespace in output */
                freespace = outlen - (q - out);
                if (freespace <= 1) {
                        return -1;
                }
-               c = *p;
-               if (c != '%') {
-                       *q++ = *p;
+
+               /*
+                *      Non-% get copied as-is.
+                */
+               if (*p != '%') {
+                       *q++ = *p++;
+                       continue;
+               }
+               p++;
+               if (!*p) {      /* % and then EOS --> % */
+                       *q++ = '%';
+                       break;
+               }
+
+               if (freespace <= 2) return -1;
+
+               /*
+                *      We need TWO %% in a row before we do our expansions.
+                *      If we only get one, just copy the %s as-is.
+                */
+               if (*p != '%') {
+                       *q++ = '%';
+                       *q++ = *p++;
                        continue;
                }
-               if (*++p == '\0') break;
-               if (c == '%') switch (*p) {
+               p++;
+               if (!*p) {
+                       *q++ = '%';
+                       *q++ = '%';
+                       break;
+               }
+
+               if (freespace <= 3) return -1;
+
+               switch (*p) {
                        case 'b': /* last_reset */
                                snprintf(tmpdt, sizeof(tmpdt), "%" PRId64, (int64_t) inst->last_reset);
                                strlcpy(q, tmpdt, freespace);
                                q += strlen(q);
+                               p++;
                                break;
                        case 'e': /* reset_time */
                                snprintf(tmpdt, sizeof(tmpdt), "%" PRId64, (int64_t) inst->reset_time);
                                strlcpy(q, tmpdt, freespace);
                                q += strlen(q);
+                               p++;
                                break;
                        case 'k': /* Key Name */
                                WARN("Please replace '%%k' with '${key}'");
                                strlcpy(q, inst->key_name, freespace);
                                q += strlen(q);
+                               p++;
                                break;
+
+                               /*
+                                *      %%s gets copied over as-is.
+                                */
                        default:
                                *q++ = '%';
-                               *q++ = *p;
+                               *q++ = '%';
+                               *q++ = *p++;
                                break;
                }
        }
@@ -346,6 +382,51 @@ static int sqlcounter_cmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *r
        return 0;
 }
 
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
+{
+       rlm_sqlcounter_t *inst = instance;
+       DICT_ATTR const *da;
+       ATTR_FLAGS flags;
+
+       memset(&flags, 0, sizeof(flags));
+       flags.compare = 1;      /* ugly hack */
+       da = dict_attrbyname(inst->counter_name);
+       if (da && (da->type != PW_TYPE_INTEGER64)) {
+               cf_log_err_cs(conf, "Counter attribute %s MUST be integer64", inst->counter_name);
+               return -1;
+       }
+
+       if (!da && (dict_addattr(inst->counter_name, -1, 0, PW_TYPE_INTEGER64, flags) < 0)) {
+               cf_log_err_cs(conf, "Failed to create counter attribute %s: %s", inst->counter_name, fr_strerror());
+               return -1;
+       }
+
+       /*
+        *  Register the counter comparison operation.
+        */
+       if (paircompare_register_byname(inst->counter_name, NULL, true, sqlcounter_cmp, inst) < 0) {
+               cf_log_err_cs(conf, "Failed registering counter attribute %s: %s", inst->counter_name, fr_strerror());
+               return -1;
+       }
+
+       inst->dict_attr = dict_attrbyname(inst->counter_name);
+       if (!inst->dict_attr) {
+               cf_log_err_cs(conf, "Failed to find counter attribute %s", inst->counter_name);
+               return -1;
+       }
+
+       /*
+        *  Create a new attribute for the check item.
+        */
+       flags.compare = 0;
+       if ((dict_addattr(inst->limit_name, -1, 0, PW_TYPE_INTEGER64, flags) < 0) ||
+           !dict_attrbyname(inst->limit_name)) {
+               cf_log_err_cs(conf, "Failed to create check attribute %s: %s", inst->limit_name, fr_strerror());
+               return -1;
+       }
+
+       return 0;
+}
 
 /*
  *     Do any per-module initialization that is separate to each
@@ -361,7 +442,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_sqlcounter_t *inst = instance;
        DICT_ATTR const *da;
-       ATTR_FLAGS flags;
        time_t now;
 
        rad_assert(inst->query && *inst->query);
@@ -380,29 +460,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
        inst->reply_attr = da;
 
-       /*
-        *  Create a new attribute for the counter.
-        */
-       rad_assert(inst->counter_name && *inst->counter_name);
-       memset(&flags, 0, sizeof(flags));
-       if ((dict_addattr(inst->counter_name, -1, 0, PW_TYPE_INTEGER64, flags) < 0) ||
-           !(da = dict_attrbyname(inst->counter_name))) {
-               cf_log_err_cs(conf, "Failed to create counter attribute %s: %s", inst->counter_name, fr_strerror());
-               return -1;
-       }
-
-       inst->dict_attr = da;
-
-       /*
-        *  Create a new attribute for the check item.
-        */
-       rad_assert(inst->limit_name && *inst->limit_name);
-       if ((dict_addattr(inst->limit_name, -1, 0, PW_TYPE_INTEGER64, flags) < 0) ||
-           !(da = dict_attrbyname(inst->limit_name))) {
-               cf_log_err_cs(conf, "Failed to create check attribute %s: %s", inst->limit_name, fr_strerror());
-               return -1;
-       }
-
        now = time(NULL);
        inst->reset_time = 0;
 
@@ -421,11 +478,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                return -1;
        }
 
-       /*
-        *  Register the counter comparison operation.
-        */
-       paircompare_register(inst->dict_attr, NULL, true, sqlcounter_cmp, inst);
-
        return 0;
 }
 
@@ -469,7 +521,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        if ((inst->key_attr->vendor == 0) && (inst->key_attr->attr == PW_USER_NAME)) {
                key_vp = request->username;
        } else {
-               key_vp = pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
+               key_vp = fr_pair_find_by_da(request->packet->vps, inst->key_attr, TAG_ANY);
        }
        if (!key_vp) {
                RWDEBUG2("Couldn't find key attribute, request:%s, doing nothing...", inst->key_attr->name);
@@ -483,7 +535,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                return rcode;
        }
 
-       limit = pair_find_by_da(request->config_items, da, TAG_ANY);
+       limit = fr_pair_find_by_da(request->config, da, TAG_ANY);
        if (limit == NULL) {
                /* Yes this really is 'check' as distinct from control */
                RWDEBUG2("Couldn't find check attribute, control:%s, doing nothing...", inst->limit_name);
@@ -523,7 +575,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        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);
+               pair_make_reply("Reply-Message", msg, T_OP_EQ);
 
                REDEBUG2("Maximum %s usage time reached", inst->reset);
                REDEBUG2("Rejecting user, &control:%s value (%" PRIu64 ") is less than counter value (%" PRIu64 ")",
@@ -559,7 +611,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
        /*
         *      Limit the reply attribute to the minimum of the existing value, or this new one.
         */
-       reply_item = pair_find_by_da(request->reply->vps, inst->reply_attr, TAG_ANY);
+       reply_item = fr_pair_find_by_da(request->reply->vps, inst->reply_attr, TAG_ANY);
        if (reply_item) {
                if (reply_item->vp_integer64 <= res) {
                        RDEBUG2("Leaving existing &reply:%s value of %" PRIu64, inst->reply_attr->name,
@@ -568,7 +620,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                        return RLM_MODULE_OK;
                }
        } else {
-               reply_item = radius_paircreate(request->reply, &request->reply->vps, inst->reply_attr->attr,
+               reply_item = radius_pair_create(request->reply, &request->reply->vps, inst->reply_attr->attr,
                                               inst->reply_attr->vendor);
        }
        reply_item->vp_integer64 = res;
@@ -589,22 +641,15 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
  */
 extern module_t rlm_sqlcounter;
 module_t rlm_sqlcounter = {
-       RLM_MODULE_INIT,
-       "rlm_sqlcounter",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_sqlcounter_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "sqlcounter",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_sqlcounter_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
 
diff --git a/src/modules/rlm_sqlhpwippool/all.mk b/src/modules/rlm_sqlhpwippool/all.mk
new file mode 100644 (file)
index 0000000..dc22d89
--- /dev/null
@@ -0,0 +1,11 @@
+TARGETNAME     := rlm_sqlhpwippool
+
+ifneq "$(TARGETNAME)" ""
+TARGET         := $(TARGETNAME).a
+endif
+
+SOURCES                := $(TARGETNAME).c
+
+SRC_CFLAGS     := 
+SRC_CFLAGS     += -I$(top_builddir)/src/modules/rlm_sql
+TGT_LDLIBS     := 
index f24846c..32b03bc 100644 (file)
@@ -77,7 +77,7 @@ static CONF_PARSER module_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 
 int nvp_log(unsigned int line, rlm_sqlhpwippool_t *data, int lvl, char const *fmt, ...) CC_HINT(format (printf, 4, 5));
@@ -155,14 +155,14 @@ static int nvp_select(unsigned int line, rlm_sqlhpwippool_t *data,
        }
        va_end(ap);
 
-       if ((data->db->sql_store_result)(sqlsock, data->sql_inst->config)) {
+       if (data->db->sql_store_result && (data->db->sql_store_result)(sqlsock, data->sql_inst->config)) {
                nvp_log(__LINE__, data, L_ERR,
                        "nvp_select(): error while saving results of query from line %u",
                        line);
                return 0;
        }
 
-       if ((data->db->sql_num_rows)(sqlsock, data->sql_inst->config) < 1) {
+       if (data->db->sql_num_rows && ((data->db->sql_num_rows)(sqlsock, data->sql_inst->config) < 1)) {
                nvp_log(__LINE__, data, L_DBG,
                        "nvp_select(): no results in query from line %u", line);
                return -1;
@@ -296,7 +296,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        inst->sincesync = 0;
 
-       sql_inst = find_module_instance(cf_section_find("modules"), (inst->sql_module_instance), true);
+       sql_inst = module_instantiate(cf_section_find("modules"), (inst->sql_module_instance));
        if (!sql_inst) {
                nvp_log(__LINE__, inst, L_ERR,
                        "mod_instantiate(): cannot find module instance "
@@ -340,7 +340,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        rlm_sqlhpwippool_t *inst = (rlm_sqlhpwippool_t *) instance;
 
        /* if IP is already there, then nothing to do */
-       vp = pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY);
        if (vp) {
                nvp_log(__LINE__, inst, L_DBG,
                        "mod_post_auth(): IP address "
@@ -349,7 +349,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        }
 
        /* if no pool name, we don't need to do anything */
-       vp = pairfind(request->reply->vps, ASN_IP_POOL_NAME, VENDORPEC_ASN, TAG_ANY);
+       vp = fr_pair_find_by_num(request->reply->vps, ASN_IP_POOL_NAME, VENDORPEC_ASN, TAG_ANY);
        if (vp) {
                pname = vp->vp_strvalue;
                nvp_log(__LINE__, inst, L_DBG,
@@ -363,7 +363,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        }
 
        /* if no NAS IP address, assign 0 */
-       vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
        if (vp) {
                nasip = ntohl(vp->vp_ipaddr);
        }
@@ -636,7 +636,7 @@ end_gid:
        }
 
        /* add IP address to reply packet */
-       vp = radius_paircreate(request->reply, &request->reply->vps,
+       vp = radius_pair_create(request->reply, &request->reply->vps,
                               PW_FRAMED_IP_ADDRESS, 0);
        vp->vp_ipaddr = ip.s_addr;
 
@@ -658,7 +658,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        rlm_sqlhpwippool_t *inst = (rlm_sqlhpwippool_t *) instance;
 
        /* if no unique session ID, don't even try */
-       vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID, 0, TAG_ANY);
        if (vp) {
                sessid = vp->vp_strvalue;
        }
@@ -668,7 +668,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
                return RLM_MODULE_FAIL;
        }
 
-       vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
        if (vp) {
                acct_type = vp->vp_integer;
        }
@@ -698,7 +698,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        switch (acct_type) {
        case PW_STATUS_START:
        case PW_STATUS_ALIVE:
-               vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY);
                if (!vp) {
                        nvp_log(__LINE__, inst, L_ERR, "mod_accounting(): no framed IP");
                        fr_connection_release(inst->sql_inst->pool, sqlsock);
@@ -738,7 +738,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
 
        case PW_STATUS_ACCOUNTING_OFF:
        case PW_STATUS_ACCOUNTING_ON:
-               vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
+               vp = fr_pair_find_by_num(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
                if (!vp) {
                        nvp_log(__LINE__, inst, L_ERR, "mod_accounting(): no NAS IP");
                        fr_connection_release(inst->sql_inst->pool, sqlsock);
@@ -769,21 +769,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
 
 extern module_t rlm_sqlhpwippool;
 module_t rlm_sqlhpwippool = {
-       RLM_MODULE_INIT,
-       "sqlhpwippool",                 /* name */
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_sqlhpwippool_t),
-       module_config,
-       mod_instantiate,        /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* preaccounting */
-               mod_accounting,/* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_post_auth   /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "sqlhpwippool",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_sqlhpwippool_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
diff --git a/src/modules/rlm_sqlippool/all.mk b/src/modules/rlm_sqlippool/all.mk
new file mode 100644 (file)
index 0000000..6a014f0
--- /dev/null
@@ -0,0 +1,11 @@
+TARGETNAME     := rlm_sqlippool
+
+ifneq "$(TARGETNAME)" ""
+TARGET         := $(TARGETNAME).a
+endif
+
+SOURCES                := $(TARGETNAME).c
+
+SRC_CFLAGS     := 
+SRC_CFLAGS     += -I$(top_builddir)/src/modules/rlm_sql
+TGT_LDLIBS     := 
index 358134d..c99d039 100644 (file)
@@ -100,8 +100,7 @@ static CONF_PARSER message_config[] = {
        { "clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_clear), NULL },
        { "failed", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_failed), NULL },
        { "nopool", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, log_nopool), NULL },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -133,13 +132,13 @@ static CONF_PARSER module_config[] = {
        { "allocate_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, allocate_begin), "START TRANSACTION" },
 
        { "allocate-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_clear), NULL },
-       { "allocate_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_clear), ""  },
+       { "allocate_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, allocate_clear), ""  },
 
        { "allocate-find", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_find), NULL },
        { "allocate_find", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_find), ""  },
 
        { "allocate-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_update), NULL },
-       { "allocate_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_update), ""  },
+       { "allocate_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, allocate_update), ""  },
 
        { "allocate-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_commit), NULL },
        { "allocate_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, allocate_commit), "COMMIT" },
@@ -153,7 +152,7 @@ static CONF_PARSER module_config[] = {
        { "start_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, start_begin), "START TRANSACTION" },
 
        { "start-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_update), NULL },
-       { "start_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, start_update), ""  },
+       { "start_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, start_update), ""  },
 
        { "start-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_commit), NULL },
        { "start_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, start_commit), "COMMIT" },
@@ -163,7 +162,7 @@ static CONF_PARSER module_config[] = {
        { "alive_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, alive_begin), "START TRANSACTION" },
 
        { "alive-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_update), NULL },
-       { "alive_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, alive_update), ""  },
+       { "alive_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, alive_update), ""  },
 
        { "alive-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_commit), NULL },
        { "alive_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, alive_commit), "COMMIT" },
@@ -173,7 +172,7 @@ static CONF_PARSER module_config[] = {
        { "stop_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, stop_begin), "START TRANSACTION" },
 
        { "stop-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_clear), NULL },
-       { "stop_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, stop_clear), ""  },
+       { "stop_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, stop_clear), ""  },
 
        { "stop-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_commit), NULL },
        { "stop_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, stop_commit), "COMMIT" },
@@ -183,7 +182,7 @@ static CONF_PARSER module_config[] = {
        { "on_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, on_begin), "START TRANSACTION" },
 
        { "on-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_clear), NULL },
-       { "on_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, on_clear), ""  },
+       { "on_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, on_clear), ""  },
 
        { "on-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_commit), NULL },
        { "on_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, on_commit), "COMMIT" },
@@ -193,14 +192,13 @@ static CONF_PARSER module_config[] = {
        { "off_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, off_begin), "START TRANSACTION" },
 
        { "off-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_clear), NULL },
-       { "off_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_REQUIRED, rlm_sqlippool_t, off_clear), ""  },
+       { "off_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, off_clear), ""  },
 
        { "off-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_commit), NULL },
        { "off_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, off_commit), "COMMIT" },
 
        { "messages", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) message_config },
-
-       { NULL, -1, 0, NULL, NULL }
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -302,7 +300,7 @@ static int sqlippool_command(char const * fmt, rlm_sql_handle_t * handle, rlm_sq
        /*
         *      If we don't have a command, do nothing.
         */
-       if (!*fmt) return 0;
+       if (!fmt || !*fmt) return 0;
 
        /*
         *      @todo this needs to die (should just be done in xlat expansion)
@@ -414,8 +412,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        } else {
                inst->pool_name = talloc_typed_strdup(inst, "ippool");
        }
-       sql_inst = find_module_instance(cf_section_find("modules"),
-                                      inst->sql_instance_name, true);
+       sql_inst = module_instantiate(cf_section_find("modules"),
+                                       inst->sql_instance_name);
        if (!sql_inst) {
                cf_log_err_cs(conf, "failed to find sql instance named %s",
                           inst->sql_instance_name);
@@ -454,7 +452,7 @@ static int do_logging(REQUEST *request, char const *str, int rcode)
                return rcode;
        }
 
-       pairmake_config("Module-Success-Message", expanded, T_OP_SET);
+       pair_make_config("Module-Success-Message", expanded, T_OP_SET);
 
        talloc_free(expanded);
 
@@ -477,13 +475,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        /*
         *      If there is a Framed-IP-Address attribute in the reply do nothing
         */
-       if (pairfind(request->reply->vps, inst->framed_ip_address, 0, TAG_ANY) != NULL) {
+       if (fr_pair_find_by_num(request->reply->vps, inst->framed_ip_address, 0, TAG_ANY) != NULL) {
                RDEBUG("Framed-IP-Address already exists");
 
                return do_logging(request, inst->log_exists, RLM_MODULE_NOOP);
        }
 
-       if (pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY) == NULL) {
+       if (fr_pair_find_by_num(request->config, PW_POOL_NAME, 0, TAG_ANY) == NULL) {
                RDEBUG("No Pool-Name defined");
 
                return do_logging(request, inst->log_nopool, RLM_MODULE_NOOP);
@@ -578,8 +576,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *      See if we can create the VP from the returned data.  If not,
         *      error out.  If so, add it to the list.
         */
-       vp = paircreate(request->reply, inst->framed_ip_address, 0);
-       if (pairparsevalue(vp, allocation, allocation_len) < 0) {
+       vp = fr_pair_afrom_num(request->reply, inst->framed_ip_address, 0);
+       if (fr_pair_value_from_str(vp, allocation, allocation_len) < 0) {
                DO(allocate_commit);
 
                RDEBUG("Invalid IP number [%s] returned from instbase query.", allocation);
@@ -588,7 +586,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        }
 
        RDEBUG("Allocated IP %s", allocation);
-       pairadd(&request->reply->vps, vp);
+       fr_pair_add(&request->reply->vps, vp);
 
        /*
         *      UPDATE
@@ -665,7 +663,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        rlm_sqlippool_t *inst = (rlm_sqlippool_t *) instance;
        rlm_sql_handle_t *handle;
 
-       vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
+       vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
        if (!vp) {
                RDEBUG("Could not find account status type in packet");
                return RLM_MODULE_NOOP;
@@ -733,21 +731,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
  */
 extern module_t rlm_sqlippool;
 module_t rlm_sqlippool = {
-       RLM_MODULE_INIT,
-       "sqlippool",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_sqlippool_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               NULL,                   /* authorization */
-               NULL,                   /* preaccounting */
-               mod_accounting,         /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "sqlippool",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_sqlippool_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .methods = {
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
index 5e8be65..22ecb60 100644 (file)
@@ -50,8 +50,7 @@ static const CONF_PARSER module_config[] = {
        { "boolean", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_test_t, boolean), "no" },
        { "string", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_test_t, string), NULL },
        { "ipaddr", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_test_t, ipaddr), "*" },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 static int rlm_test_cmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *thing, VALUE_PAIR *check,
@@ -209,25 +208,20 @@ static int mod_detach(UNUSED void *instance)
  */
 extern module_t rlm_test;
 module_t rlm_test = {
-       RLM_MODULE_INIT,
-       "test",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_test_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       mod_detach,                     /* detach */
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "test",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_test_t),
+       .config         = module_config,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach,
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize,
 #ifdef WITH_ACCOUNTING
-               mod_preacct,            /* preaccounting */
-               mod_accounting,         /* accounting */
-               mod_checksimul,         /* checksimul */
-#else
-               NULL, NULL, NULL,
+               [MOD_PREACCT]           = mod_preacct,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_SESSION]           = mod_checksimul
 #endif
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
        },
 };
index af8dce0..9f4b6ae 100644 (file)
@@ -57,7 +57,7 @@ typedef struct rlm_unbound_t {
 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 */
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -388,6 +388,34 @@ static void log_spew(UNUSED fr_event_list_t *el, UNUSED int sock, void *ctx)
 
 #endif
 
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
+{
+       rlm_unbound_t *inst = instance;
+
+       inst->name = cf_section_name2(conf);
+       if (!inst->name) {
+               inst->name = cf_section_name1(conf);
+       }
+
+       if (inst->timeout > 10000) {
+               cf_log_err_cs(conf, "timeout must be 0 to 10000");
+               return -1;
+       }
+
+       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)) {
+               cf_log_err_cs(conf, "Failed registering xlats");
+               return -1;
+       }
+
+       return 0;
+}
+
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_unbound_t *inst = instance;
@@ -406,19 +434,9 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        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);
+               cf_log_err_cs(conf, "ub_ctx_create failed");
                return -1;
        }
 
@@ -439,11 +457,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        /*      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. */
+       /*      TODO: log until fork when stdout or stderr and !rad_debug_lvl. */
        log_level = 0;
 
-       if (debug_flag > 0) {
-               log_level = debug_flag;
+       if (rad_debug_lvl > 0) {
+               log_level = rad_debug_lvl;
 
        } else if (main_config.debug_level > 0) {
                log_level = main_config.debug_level;
@@ -481,7 +499,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        switch (default_log.dst) {
        case L_DST_STDOUT:
-               if (!debug_flag) {
+               if (!rad_debug_lvl) {
                        log_dst = L_DST_NULL;
                        break;
                }
@@ -490,7 +508,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                break;
 
        case L_DST_STDERR:
-               if (!debug_flag) {
+               if (!rad_debug_lvl) {
                        log_dst = L_DST_NULL;
                        break;
                }
@@ -572,7 +590,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                if (optval && strlen(optval)) {
                        log_dst = L_DST_FILES;
 
-               } else if (!debug_flag) {
+               } else if (!rad_debug_lvl) {
                        log_dst = L_DST_NULL;
                }
 
@@ -586,13 +604,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                 * dup it so libunbound doesn't close it on us.
                 */
                if (log_fd == -1) {
-                       ERROR("rlm_unbound (%s): Could not dup fd", inst->name);
+                       cf_log_err_cs(conf, "Could not dup fd");
                        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);
+                       cf_log_err_cs(conf, "error setting up log stream");
                        goto error_nores;
                }
 
@@ -626,7 +644,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                 */
                if (pipe(inst->log_pipe)) {
                error_pipe:
-                       ERROR("rlm_unbound (%s): Error setting up log pipes", inst->name);
+                       cf_log_err_cs(conf, "Error setting up log pipes");
                        goto error_nores;
                }
 
@@ -651,7 +669,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                        if (!inst->log_pipe_stream[0]) {
                                close(inst->log_pipe[0]);
                        }
-                       ERROR("rlm_unbound (%s): Error setting up log stream", inst->name);
+                       cf_log_err_cs(conf, "Error setting up log stream");
                        goto error_nores;
                }
 
@@ -659,7 +677,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                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);
+                       cf_log_err_cs(conf, "could not insert log fd");
                        goto error_nores;
                }
 
@@ -683,27 +701,17 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        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);
+                       cf_log_err_cs(conf, "could not insert async fd");
                        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);
-               goto error_nores;
-       }
        return 0;
 
  error:
-       ERROR("rlm_unbound (%s): %s", inst->name, ub_strerror(res));
+       cf_log_err_cs(conf, "%s", ub_strerror(res));
 
  error_nores:
        if (log_fd > -1) close(log_fd);
@@ -749,13 +757,12 @@ static int mod_detach(UNUSED void *instance)
 
 extern module_t rlm_unbound;
 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 },
+       .magic          = RLM_MODULE_INIT,
+       .name           = "unbound",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_unbound_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
+       .detach         = mod_detach
 };
index 3a79287..590e591 100644 (file)
@@ -55,19 +55,17 @@ USES_APPLE_DEPRECATED_API
 #include       <freeradius-devel/modules.h>
 #include       <freeradius-devel/sysutmp.h>
 
-static char trans[64] =
-   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static char trans[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 #define ENC(c) trans[c]
 
-struct unix_instance {
+typedef struct rlm_unix {
        char const *name;       //!< Instance name.
        char const *radwtmp;
-};
+} rlm_unix_t;
 
 static const CONF_PARSER module_config[] = {
-       { "radwtmp", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, struct unix_instance, radwtmp), "NULL" },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       { "radwtmp", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_unix_t, radwtmp), "NULL" },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -80,7 +78,7 @@ static int groupcmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *
        struct passwd   *pwd;
        struct group    *grp;
        char            **member;
-       int             retval;
+       int             retval = -1;
 
        /*
         *      No user name, can't compare.
@@ -88,19 +86,29 @@ static int groupcmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *
        if (!request->username) return -1;
 
        if (rad_getpwnam(request, &pwd, request->username->vp_strvalue) < 0) {
-               RERROR("%s", fr_strerror());
+               RDEBUG("%s", fr_strerror());
                return -1;
        }
+
        if (rad_getgrnam(request, &grp, check->vp_strvalue) < 0) {
-               RERROR("%s", fr_strerror());
+               RDEBUG("%s", fr_strerror());
                talloc_free(pwd);
                return -1;
        }
 
-       retval = (pwd->pw_gid == grp->gr_gid) ? 0 : -1;
-       if (retval < 0) {
+       /*
+        *      The users default group isn't the one we're looking for,
+        *      look through the list of group members.
+        */
+       if (pwd->pw_gid == grp->gr_gid) {
+               retval = 0;
+
+       } else {
                for (member = grp->gr_mem; *member && retval; member++) {
-                       if (strcmp(*member, pwd->pw_name) == 0) retval = 0;
+                       if (strcmp(*member, pwd->pw_name) == 0) {
+                               retval = 0;
+                               break;
+                       }
                }
        }
 
@@ -115,9 +123,9 @@ static int groupcmp(UNUSED void *instance, REQUEST *request, UNUSED VALUE_PAIR *
 /*
  *     Read the config
  */
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
-       struct unix_instance *inst = instance;
+       rlm_unix_t *inst = instance;
 
        DICT_ATTR const *group_da, *user_name_da;
 
@@ -137,9 +145,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                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(group_da, user_name_da, false, groupcmp, inst);
+
 #ifdef PW_GROUP_NAME /* compat */
        {
                DICT_ATTR const *group_name_da;
@@ -153,6 +163,12 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 #endif
 
+       if (paircompare_register_byname("Unix-Group", user_name_da, false, groupcmp, inst) < 0) {
+               ERROR("rlm_unix (%s): Failed registering Unix-Group: %s", inst->name,
+                     fr_strerror());
+               return -1;
+       }
+
        return 0;
 }
 
@@ -298,7 +314,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
        if (encrypted_pass[0] == 0)
                return RLM_MODULE_NOOP;
 
-       vp = pairmake_config("Crypt-Password", encrypted_pass, T_OP_SET);
+       vp = pair_make_config("Crypt-Password", encrypted_pass, T_OP_SET);
        if (!vp) return RLM_MODULE_FAIL;
 
        return RLM_MODULE_UPDATED;
@@ -355,7 +371,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
 #endif
        uint32_t        nas_port = 0;
        bool            port_seen = true;
-       struct unix_instance *inst = (struct unix_instance *) instance;
+       rlm_unix_t *inst = (rlm_unix_t *) instance;
 
        /*
         *      No radwtmp.  Don't do anything.
@@ -373,7 +389,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
        /*
         *      Which type is this.
         */
-       if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY))==NULL) {
+       if ((vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY))==NULL) {
                RDEBUG("no Accounting-Status-Type attribute in request");
                return RLM_MODULE_NOOP;
        }
@@ -390,7 +406,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
         *      We're only interested in accounting messages
         *      with a username in it.
         */
-       if (pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY) == NULL)
+       if (fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY) == NULL)
                return RLM_MODULE_NOOP;
 
        t = request->timestamp;
@@ -517,21 +533,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ
 /* globally exported name */
 extern module_t rlm_unix;
 module_t rlm_unix = {
-       RLM_MODULE_INIT,
-       "System",
-       RLM_TYPE_THREAD_UNSAFE,
-       sizeof(struct unix_instance),
-       module_config,
-       mod_instantiate,                /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,               /* authentication */
-               mod_authorize,       /* authorization */
-               NULL,            /* preaccounting */
-               mod_accounting,      /* accounting */
-               NULL,             /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "unix",
+       .type           = RLM_TYPE_THREAD_UNSAFE,
+       .inst_size      = sizeof(rlm_unix_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_ACCOUNTING]        = mod_accounting
        },
 };
index ea44f80..605d6b0 100644 (file)
@@ -150,7 +150,7 @@ static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *
                goto nothing;
        }
 
-       cast = pairalloc(request, da);
+       cast = fr_pair_afrom_da(request, da);
        if (!cast) goto nothing;
 
        memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]);
@@ -192,8 +192,10 @@ static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *
 /*
  *     Register the xlats
  */
-static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
 {
+       if (cf_section_name2(conf)) return 0;
+
        xlat_register("unpack", unpack_xlat, NULL, instance);
 
        return 0;
@@ -210,19 +212,8 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
  */
 extern module_t rlm_unpack;
 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 */
-       },
+       .magic          = RLM_MODULE_INIT,
+       .name           = "unpack",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .bootstrap      = mod_bootstrap
 };
index 7949dfa..d2031c6 100644 (file)
@@ -41,7 +41,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_utf8_clean(UNUSED void *instance, REQUES
                if (vp->da->type != PW_TYPE_STRING) continue;
 
                for (i = 0; i < vp->vp_length; i += len) {
-                       len = fr_utf8_char(&vp->vp_octets[i]);
+                       len = fr_utf8_char(&vp->vp_octets[i], -1);
                        if (len == 0) return RLM_MODULE_FAIL;
                }
        }
@@ -60,25 +60,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_utf8_clean(UNUSED void *instance, REQUES
  */
 extern module_t rlm_utf8;
 module_t rlm_utf8 = {
-       RLM_MODULE_INIT,
-       "utf8",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       0,
-       NULL,                           /* CONF_PARSER */
-       NULL,                           /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_utf8_clean,         /* authorization */
-               mod_utf8_clean,         /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "utf8",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_utf8_clean,
+               [MOD_PREACCT]           = mod_utf8_clean,
 #ifdef WITH_COA
-               , mod_utf8_clean,
-               NULL
+               [MOD_RECV_COA]          = mod_utf8_clean
 #endif
        },
 };
index db52d5b..784ae63 100644 (file)
@@ -49,9 +49,8 @@ typedef struct rlm_wimax_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-  { "delete_mppe_keys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_wimax_t, delete_mppe_keys), "no" },
-
-  { NULL, -1, 0, NULL, NULL }          /* end the list */
+       { "delete_mppe_keys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_wimax_t, delete_mppe_keys), "no" },
+       CONF_PARSER_TERMINATOR
 };
 
 /*
@@ -67,7 +66,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST
        /*
         *      Fix Calling-Station-Id.  Damn you, WiMAX!
         */
-       vp =  pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY);
+       vp =  fr_pair_find_by_num(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY);
        if (vp && (vp->vp_length == 6)) {
                int i;
                char *p;
@@ -130,8 +129,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        uint8_t mip_rk_1[EVP_MAX_MD_SIZE], mip_rk_2[EVP_MAX_MD_SIZE];
        uint8_t mip_rk[2 * EVP_MAX_MD_SIZE];
 
-       msk = pairfind(request->reply->vps, 1129, 0, TAG_ANY);
-       emsk = pairfind(request->reply->vps, 1130, 0, TAG_ANY);
+       msk = fr_pair_find_by_num(request->reply->vps, PW_EAP_MSK, 0, TAG_ANY);
+       emsk = fr_pair_find_by_num(request->reply->vps, PW_EAP_EMSK, 0, TAG_ANY);
        if (!msk || !emsk) {
                RDEBUG("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys");
                return RLM_MODULE_NOOP;
@@ -142,12 +141,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *      the WiMAX-MSK so that the client has a key available.
         */
        if (inst->delete_mppe_keys) {
-               pairdelete(&request->reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
-               pairdelete(&request->reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_delete_by_num(&request->reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY);
+               fr_pair_delete_by_num(&request->reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY);
 
-               vp = pairmake_reply("WiMAX-MSK", NULL, T_OP_EQ);
+               vp = pair_make_reply("WiMAX-MSK", NULL, T_OP_EQ);
                if (vp) {
-                       pairmemcpy(vp, msk->vp_octets, msk->vp_length);
+                       fr_pair_value_memcpy(vp, msk->vp_octets, msk->vp_length);
                }
        }
 
@@ -197,7 +196,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                   (mip_rk_1[2] << 8) | mip_rk_1[3]);
        if (mip_spi < 256) mip_spi += 256;
 
-       if (debug_flag) {
+       if (rad_debug_lvl) {
                int len = rk_len;
                char buffer[512];
 
@@ -215,8 +214,8 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
        /*
         *      Calculate mobility keys
         */
-       mn_nai = pairfind(request->packet->vps, 1900, 0, TAG_ANY);
-       if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900, 0, TAG_ANY);
+       mn_nai = fr_pair_find_by_num(request->packet->vps, PW_WIMAX_MN_NAI, 0, TAG_ANY);
+       if (!mn_nai) mn_nai = fr_pair_find_by_num(request->reply->vps, PW_WIMAX_MN_NAI, 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");
@@ -226,7 +225,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *      WiMAX-IP-Technology
         */
        vp = NULL;
-       if (mn_nai) vp = pairfind(request->reply->vps, 23, VENDORPEC_WIMAX, TAG_ANY);
+       if (mn_nai) vp = fr_pair_find_by_num(request->reply->vps, 23, VENDORPEC_WIMAX, TAG_ANY);
        if (!vp) {
                RWDEBUG("WiMAX-IP-Technology not found in reply");
                RWDEBUG("Not calculating MN-HA keys");
@@ -237,7 +236,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      Look for WiMAX-hHA-IP-MIP4
                 */
-               ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY);
+               ip = fr_pair_find_by_num(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY);
                if (!ip) {
                        RWDEBUG("WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-PMIP4 key");
                        break;
@@ -257,23 +256,23 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      Put MN-HA-PMIP4 into WiMAX-MN-hHA-MIP4-Key
                 */
-               vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               10, VENDORPEC_WIMAX);
                }
                if (!vp) {
                        RWDEBUG("Failed creating WiMAX-MN-hHA-MIP4-Key");
                        break;
                }
-               pairmemcpy(vp, &mip_rk_1[0], rk1_len);
+               fr_pair_value_memcpy(vp, &mip_rk_1[0], rk1_len);
 
                /*
                 *      Put MN-HA-PMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
                 */
-               vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               11, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -287,7 +286,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      Look for WiMAX-hHA-IP-MIP4
                 */
-               ip = pairfind(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY);
+               ip = fr_pair_find_by_num(request->reply->vps, 6, VENDORPEC_WIMAX, TAG_ANY);
                if (!ip) {
                        RWDEBUG("WiMAX-hHA-IP-MIP4 not found.  Cannot calculate MN-HA-CMIP4 key");
                        break;
@@ -307,23 +306,23 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      Put MN-HA-CMIP4 into WiMAX-MN-hHA-MIP4-Key
                 */
-               vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               10, VENDORPEC_WIMAX);
                }
                if (!vp) {
                        RWDEBUG("Failed creating WiMAX-MN-hHA-MIP4-Key");
                        break;
                }
-               pairmemcpy(vp, &mip_rk_1[0], rk1_len);
+               fr_pair_value_memcpy(vp, &mip_rk_1[0], rk1_len);
 
                /*
                 *      Put MN-HA-CMIP4-SPI into WiMAX-MN-hHA-MIP4-SPI
                 */
-               vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               11, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -337,7 +336,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      Look for WiMAX-hHA-IP-MIP6
                 */
-               ip = pairfind(request->reply->vps, 7, VENDORPEC_WIMAX, TAG_ANY);
+               ip = fr_pair_find_by_num(request->reply->vps, 7, VENDORPEC_WIMAX, TAG_ANY);
                if (!ip) {
                        RWDEBUG("WiMAX-hHA-IP-MIP6 not found.  Cannot calculate MN-HA-CMIP6 key");
                        break;
@@ -357,23 +356,23 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      Put MN-HA-CMIP6 into WiMAX-MN-hHA-MIP6-Key
                 */
-               vp = pairfind(request->reply->vps, 12, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, 12, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               12, VENDORPEC_WIMAX);
                }
                if (!vp) {
                        RWDEBUG("Failed creating WiMAX-MN-hHA-MIP6-Key");
                        break;
                }
-               pairmemcpy(vp, &mip_rk_1[0], rk1_len);
+               fr_pair_value_memcpy(vp, &mip_rk_1[0], rk1_len);
 
                /*
                 *      Put MN-HA-CMIP6-SPI into WiMAX-MN-hHA-MIP6-SPI
                 */
-               vp = pairfind(request->reply->vps, 13, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, 13, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               13, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -392,7 +391,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *
         *      FA-RK= H(MIP-RK, "FA-RK")
         */
-       fa_rk = pairfind(request->reply->vps, 14, VENDORPEC_WIMAX, TAG_ANY);
+       fa_rk = fr_pair_find_by_num(request->reply->vps, 14, VENDORPEC_WIMAX, TAG_ANY);
        if (fa_rk && (fa_rk->vp_length <= 1)) {
                HMAC_Init_ex(&hmac, mip_rk, rk_len, EVP_sha1(), NULL);
 
@@ -400,7 +399,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
 
                HMAC_Final(&hmac, &mip_rk_1[0], &rk1_len);
 
-               pairmemcpy(fa_rk, &mip_rk_1[0], rk1_len);
+               fr_pair_value_memcpy(fa_rk, &mip_rk_1[0], rk1_len);
        }
 
        /*
@@ -408,9 +407,9 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *      really MIP-SPI.  Clear?  Of course.  This is WiMAX.
         */
        if (fa_rk) {
-               vp = pairfind(request->reply->vps, 61, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(request->reply->vps, 61, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request->reply, &request->reply->vps,
+                       vp = radius_pair_create(request->reply, &request->reply->vps,
                                               61, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -425,7 +424,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
         *
         *      WiMAX-RRQ-MN-HA-SPI
         */
-       vp = pairfind(request->packet->vps, 20, VENDORPEC_WIMAX, TAG_ANY);
+       vp = fr_pair_find_by_num(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");
                if (!mn_nai) {
@@ -435,7 +434,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      WiMAX-RRQ-HA-IP
                 */
-               if (!pairfind(request->packet->vps, 18, VENDORPEC_WIMAX, TAG_ANY)) {
+               if (!fr_pair_find_by_num(request->packet->vps, 18, VENDORPEC_WIMAX, TAG_ANY)) {
                        RWDEBUG("HA-IP was not found!");
                }
 
@@ -443,7 +442,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
                /*
                 *      WiMAX-HA-RK-Key-Requested
                 */
-               vp = pairfind(request->packet->vps, 58, VENDORPEC_WIMAX, TAG_ANY);
+               vp = fr_pair_find_by_num(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");
                }
@@ -469,21 +468,15 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque
  */
 extern module_t rlm_wimax;
 module_t rlm_wimax = {
-       RLM_MODULE_INIT,
-       "wimax",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_wimax_t),
-       module_config,
-       NULL,                   /* instantiation */
-       NULL,                           /* detach */
-       {
-               NULL,                   /* authentication */
-               mod_authorize,  /* authorization */
-               mod_preacct,            /* preaccounting */
-               mod_accounting, /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               mod_post_auth           /* post-auth */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "wimax",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_wimax_t),
+       .config         = module_config,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_authorize,
+               [MOD_PREACCT]           = mod_preacct,
+               [MOD_ACCOUNTING]        = mod_accounting,
+               [MOD_POST_AUTH]         = mod_post_auth
        },
 };
index 01af063..49dca4a 100644 (file)
@@ -13,6 +13,7 @@
 /** Decrypt a Yubikey OTP AES block
  *
  * @param inst Module configuration.
+ * @param request The current request.
  * @param passcode string to decrypt.
  * @return one of the RLM_RCODE_* constants.
  */
@@ -32,7 +33,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char cons
                return RLM_MODULE_FAIL;
        }
 
-       key = pair_find_by_da(request->config_items, da, TAG_ANY);
+       key = fr_pair_find_by_da(request->config, da, TAG_ANY);
        if (!key) {
                REDEBUG("Yubikey-Key attribute not found in control list, can't decrypt OTP data");
                return RLM_MODULE_INVALID;
@@ -69,18 +70,18 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char cons
        /*
         *      Private ID used for validation purposes
         */
-       vp = pairmake(request, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET);
+       vp = fr_pair_make(request, &request->packet->vps, "Yubikey-Private-ID", NULL, T_OP_SET);
        if (!vp) {
                REDEBUG("Failed creating Yubikey-Private-ID");
 
                return RLM_MODULE_FAIL;
        }
-       pairmemcpy(vp, token.uid, YUBIKEY_UID_SIZE);
+       fr_pair_value_memcpy(vp, token.uid, YUBIKEY_UID_SIZE);
 
        /*
         *      Token timestamp
         */
-       vp = pairmake(request, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET);
+       vp = fr_pair_make(request, &request->packet->vps, "Yubikey-Timestamp", NULL, T_OP_SET);
        if (!vp) {
                REDEBUG("Failed creating Yubikey-Timestamp");
 
@@ -92,7 +93,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char cons
        /*
         *      Token random
         */
-       vp = pairmake(request, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET);
+       vp = fr_pair_make(request, &request->packet->vps, "Yubikey-Random", NULL, T_OP_SET);
        if (!vp) {
                REDEBUG("Failed creating Yubikey-Random");
 
@@ -107,7 +108,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char cons
         */
        counter = (yubikey_counter(token.ctr) << 16) | token.use;
 
-       vp = pairmake(request, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET);
+       vp = fr_pair_make(request, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET);
        if (!vp) {
                REDEBUG("Failed creating Yubikey-Counter");
 
@@ -119,7 +120,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char cons
        /*
         *      Now we check for replay attacks
         */
-       vp = pair_find_by_da(request->config_items, da, TAG_ANY);
+       vp = fr_pair_find_by_da(request->config, da, TAG_ANY);
        if (!vp) {
                RWDEBUG("Yubikey-Counter not found in control list, skipping replay attack checks");
                return RLM_MODULE_OK;
index 53ef32a..b444b97 100644 (file)
@@ -41,7 +41,7 @@ RCSID("$Id$")
 static const CONF_PARSER validation_config[] = {
        { "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 */
+       CONF_PARSER_TERMINATOR
 };
 #endif
 
@@ -53,7 +53,7 @@ static const CONF_PARSER module_config[] = {
 #ifdef HAVE_YKCLIENT
        { "validation", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) validation_config },
 #endif
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
+       CONF_PARSER_TERMINATOR
 };
 
 static char const modhextab[] = "cbdefghijklnrtuv";
@@ -127,6 +127,28 @@ static ssize_t modhex_to_hex_xlat(UNUSED void *instance, REQUEST *request, char
        return len;
 }
 
+
+static int mod_bootstrap(CONF_SECTION *conf, void *instance)
+{
+       rlm_yubikey_t *inst = instance;
+
+       inst->name = cf_section_name2(conf);
+       if (!inst->name) inst->name = cf_section_name1(conf);
+
+#ifndef HAVE_YUBIKEY
+       if (inst->decrypt) {
+               cf_log_err_cs(conf, "Requires libyubikey for OTP decryption");
+               return -1;
+       }
+#endif
+
+       if (!cf_section_name2(conf)) return 0;
+
+       xlat_register("modhextohex", modhex_to_hex_xlat, NULL, inst);
+
+       return 0;
+}
+
 /*
  *     Do any per-module initialization that is separate to each
  *     configured instance of the module.  e.g. set up connections
@@ -141,25 +163,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_yubikey_t *inst = instance;
 
-       inst->name = cf_section_name2(conf);
-       if (!inst->name) {
-               inst->name = cf_section_name1(conf);
-       }
-
-#ifndef HAVE_YUBIKEY
-       if (inst->decrypt) {
-               ERROR("rlm_yubikey (%s): Requires libyubikey for OTP decryption", inst->name);
-               return -1;
-       }
-#endif
-
        if (inst->validate) {
 #ifdef HAVE_YKCLIENT
                CONF_SECTION *cs;
 
                cs = cf_section_sub_find(conf, "validation");
                if (!cs) {
-                       ERROR("rlm_yubikey (%s): Missing validation section", inst->name);
+                       cf_log_err_cs(conf, "Missing validation section");
                        return -1;
                }
 
@@ -167,13 +177,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                        return -1;
                }
 #else
-               ERROR("rlm_yubikey (%s): Requires libykclient for OTP validation against Yubicloud servers", inst->name);
+               cf_log_err_cs(conf, "Requires libykclient for OTP validation against Yubicloud servers");
                return -1;
 #endif
        }
 
-       xlat_register("modhextohex", modhex_to_hex_xlat, NULL, inst);
-
        return 0;
 }
 
@@ -267,7 +275,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                         *      Insert a new request attribute just containing the OTP
                         *      portion.
                         */
-                       vp = pairmake_packet("Yubikey-OTP", otp, T_OP_SET);
+                       vp = pair_make_request("Yubikey-OTP", otp, T_OP_SET);
                        if (!vp) {
                                REDEBUG("Failed creating 'Yubikey-OTP' attribute");
                                return RLM_MODULE_FAIL;
@@ -279,7 +287,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
                         */
                        MEM(password = talloc_array(request->password, char, password_len + 1));
                        strlcpy(password, passcode, password_len + 1);
-                       pairstrsteal(request->password, password);
+                       fr_pair_value_strsteal(request->password, password);
 
                        RINDENT();
                        if (RDEBUG_ENABLED3) {
@@ -315,7 +323,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
 
        dval = dict_valbyname(PW_AUTH_TYPE, 0, inst->name);
        if (dval) {
-               vp = radius_paircreate(request, &request->config_items, PW_AUTH_TYPE, 0);
+               vp = radius_pair_create(request, &request->config, PW_AUTH_TYPE, 0);
                vp->vp_integer = dval->value;
        }
 
@@ -326,14 +334,14 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque
         *      It's left up to the user if they want to decode it or not.
         */
        if (inst->id_len) {
-               vp = pairmake(request, &request->packet->vps, "Yubikey-Public-ID", NULL, T_OP_SET);
+               vp = fr_pair_make(request, &request->packet->vps, "Yubikey-Public-ID", NULL, T_OP_SET);
                if (!vp) {
                        REDEBUG("Failed creating Yubikey-Public-ID");
 
                        return RLM_MODULE_FAIL;
                }
 
-               pairstrncpy(vp, passcode, inst->id_len);
+               fr_pair_value_bstrncpy(vp, passcode, inst->id_len);
        }
 
        return RLM_MODULE_OK;
@@ -359,7 +367,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                goto user_password;
        }
 
-       vp = pair_find_by_da(request->packet->vps, da, TAG_ANY);
+       vp = fr_pair_find_by_da(request->packet->vps, da, TAG_ANY);
        if (vp) {
                passcode = vp->vp_strvalue;
                len = vp->vp_length;
@@ -430,25 +438,18 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
  */
 extern module_t rlm_yubikey;
 module_t rlm_yubikey = {
-       RLM_MODULE_INIT,
-       "yubikey",
-       RLM_TYPE_THREAD_SAFE,           /* type */
-       sizeof(rlm_yubikey_t),
-       module_config,
-       mod_instantiate,                /* instantiation */
+       .magic          = RLM_MODULE_INIT,
+       .name           = "yubikey",
+       .type           = RLM_TYPE_THREAD_SAFE,
+       .inst_size      = sizeof(rlm_yubikey_t),
+       .config         = module_config,
+       .bootstrap      = mod_bootstrap,
+       .instantiate    = mod_instantiate,
 #ifdef HAVE_YKCLIENT
-       mod_detach,                     /* detach */
-#else
-       NULL,
+       .detach         = mod_detach,
 #endif
-       {
-               mod_authenticate,       /* authentication */
-               mod_authorize,          /* authorization */
-               NULL,                   /* preaccounting */
-               NULL,                   /* accounting */
-               NULL,                   /* checksimul */
-               NULL,                   /* pre-proxy */
-               NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+       .methods = {
+               [MOD_AUTHENTICATE]      = mod_authenticate,
+               [MOD_AUTHORIZE]         = mod_authorize
        },
 };
index 5827c59..6b8a151 100644 (file)
@@ -34,7 +34,7 @@ typedef struct rlm_yubikey_t {
        unsigned int            client_id;              //!< Validation API client ID.
        char const              *api_key;               //!< Validation API signing key.
        ykclient_t              *ykc;                   //!< ykclient configuration.
-       fr_connection_pool_t    *conn_pool;             //!< Connection pool instance.
+       fr_connection_pool_t    *pool;                  //!< Connection pool instance.
 #endif
 } rlm_yubikey_t;
 
index 5f97b53..7dc13d8 100644 (file)
@@ -132,8 +132,8 @@ init:
        }
 
        snprintf(prefix, sizeof(prefix), "rlm_yubikey (%s)", inst->name);
-       inst->conn_pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, prefix);
-       if (!inst->conn_pool) {
+       inst->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, prefix);
+       if (!inst->pool) {
                ykclient_done(&inst->ykc);
 
                return -1;
@@ -144,7 +144,7 @@ init:
 
 int rlm_yubikey_ykclient_detach(rlm_yubikey_t *inst)
 {
-       fr_connection_pool_delete(inst->conn_pool);
+       fr_connection_pool_free(inst->pool);
        ykclient_done(&inst->ykc);
        ykclient_global_done();
 
@@ -157,7 +157,7 @@ rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request,  char co
        ykclient_rc status;
        ykclient_handle_t *yandle;
 
-       yandle = fr_connection_get(inst->conn_pool);
+       yandle = fr_connection_get(inst->pool);
        if (!yandle) return RLM_MODULE_FAIL;
 
        /*
@@ -194,7 +194,7 @@ rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request,  char co
                }
        }
 
-       fr_connection_release(inst->conn_pool, yandle);
+       fr_connection_release(inst->pool, yandle);
 
        return rcode;
 }
index cb3ddf5..0fa9ba7 100644 (file)
@@ -38,3 +38,5 @@ rlm_unix
 rlm_sometimes
 rlm_wimax
 rlm_yubikey
+rlm_redis
+rlm_rediswho
index 99d131a..d87a22e 100644 (file)
@@ -1,4 +1,4 @@
-SUBMAKEFILES := rbmonkey.mk unit/all.mk xlat/all.mk keywords/all.mk auth/all.mk modules/all.mk
+SUBMAKEFILES := rbmonkey.mk unit/all.mk map/all.mk xlat/all.mk keywords/all.mk auth/all.mk modules/all.mk
 
 #
 #  Include all of the autoconf definitions into the Make variable space
index f226e05..284033f 100644 (file)
@@ -88,6 +88,7 @@ $(BUILD_DIR)/tests/auth/%: $(DIR)/% $(BUILD_DIR)/tests/auth/%.attrs $(TESTBINDIR
                if ! grep ERROR $< 2>&1 > /dev/null; then \
                        cat $@.log; \
                        echo "# $@.log"; \
+                       echo "TESTDIR=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/auth/ -i $@.attrs -f $@.attrs -xxx > $@.log 2>&1"; \
                        exit 1; \
                fi; \
                FOUND=$$(grep ^$< $@.log | head -1 | sed 's/:.*//;s/.*\[//;s/\].*//'); \
@@ -95,6 +96,7 @@ $(BUILD_DIR)/tests/auth/%: $(DIR)/% $(BUILD_DIR)/tests/auth/%.attrs $(TESTBINDIR
                if [ "$$EXPECTED" != "$$FOUND" ]; then \
                        cat $@.log; \
                        echo "# $@.log"; \
+                       echo "TESTDIR=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/auth/ -i $@.attrs -f $@.attrs -xxx > $@.log 2>&1"; \
                        exit 1; \
                fi \
        fi
diff --git a/src/tests/bob b/src/tests/bob
new file mode 100644 (file)
index 0000000..f9398ad
--- /dev/null
@@ -0,0 +1,2 @@
+User-Name = "bob"
+User-Password = "bob"
index edbc020..2d99ce1 100644 (file)
@@ -5,5 +5,5 @@ network={
        key_mgmt=NONE
        eap=MD5
        identity="bob"
-       password="hello"
+       password="bob"
 }
index 0d1b31e..86cdda7 100644 (file)
@@ -7,7 +7,7 @@
 network={
        key_mgmt=WPA-EAP
        eap=TLS
-       identity="user@example.com"
+       identity="user@example.org"
        ca_cert="../../raddb/certs/ca.pem"
        client_cert="../../raddb/certs/client.crt"
        private_key="../../raddb/certs/client.key"
index a1ef3a9..c93c573 100644 (file)
@@ -1,5 +1,5 @@
 #
-#   eapol_test -c eap-ttls-mschapv2.conf -s testing123
+#   eapol_test -c eap-ttls-eap-mschapv2.conf -s testing123
 #
 network={
        ssid="example"
index 4c14cc3..8d38b72 100644 (file)
@@ -6,6 +6,7 @@ network={
        eap=TTLS
        identity="bob"
        anonymous_identity="anonymous"
+       ca_cert="../../raddb/certs/ca.pem"
        password="bob"
        phase2="auth=PAP"
 }
index a0f0258..c261cbe 100644 (file)
@@ -22,7 +22,7 @@
 #
 #      The per-request variables are of the form %{Attribute-Name}, and
 #      are taken from the values of the attribute in the incoming
-#      request.  See 'doc/variables.txt' for more information.
+#      request.  See 'doc/configuration/variables.rst' for more information.
 
 prefix = /elros/mcr/root
 exec_prefix = ${prefix}
@@ -141,7 +141,7 @@ max_request_time = 30
 #  cached reply.
 #
 #  If this value is set too low, then duplicate requests from the NAS
-#  MAY NOT be detected, and will instead be handled as seperate requests.
+#  MAY NOT be detected, and will instead be handled as separate requests.
 #
 #  If this value is set too high, then the server will cache too many
 #  requests, and some new requests may get blocked.  (See 'max_requests'.)
@@ -734,8 +734,8 @@ modules {
        #            Field marked as '*' is key field. That is, the parameter
        #            with this name from the request is used to search for
        #            the record from passwd file
-       #            Attribute marked as '=' is added to reply_itmes instead
-       #            of default configure_itmes
+       #            Attribute marked as '=' is added to reply_items instead
+       #            of default configure_items
        #            Attribute marked as '~' is added to request_items
        #
        #            Field marked as ',' may contain a comma separated list
@@ -888,7 +888,7 @@ modules {
                #  through a 'log rotation'
                #
                #  If your detail files are large, you may also want
-               #  to add a ':%H' (see doc/variables.txt) to the end
+               #  to add a ':%H' (see doc/configuration/variables.rst) to the end
                #  of it, to create a new detail file every hour, e.g.:
                #
                #   ..../detail-%Y%m%d:%H
@@ -1104,7 +1104,7 @@ modules {
        #
        #  The RADIUS attributes from the user request will be placed
        #  into environment variables of the executed program, as
-       #  described in 'doc/variables.txt'
+       #  described in 'doc/configuration/variables.rst'
        #
        exec {
                wait = yes
index ab35bc2..369bb7b 100644 (file)
@@ -86,7 +86,7 @@ KEYWORD_LIBS  := $(addsuffix .la,$(addprefix rlm_,$(KEYWORD_MODULES))) rlm_exampl
 #  Otherwise, check the log file for a parse error which matches the
 #  ERROR line in the input.
 #
-$(BUILD_DIR)/tests/keywords/%: $(DIR)/% $(BUILD_DIR)/tests/keywords/%.attrs $(TESTBINDIR)/unittest | $(BUILD_DIR)/tests/keywords $(KEYWORD_RADDB) $(KEYWORD_LIBS) build.raddb rlm_cache_rbtree.la rlm_test.la
+$(BUILD_DIR)/tests/keywords/%: $(DIR)/% $(BUILD_DIR)/tests/keywords/%.attrs $(TESTBINDIR)/unittest | $(BUILD_DIR)/tests/keywords $(KEYWORD_RADDB) $(KEYWORD_LIBS) build.raddb rlm_cache_rbtree.la rlm_test.la rlm_unix.la
        @echo UNIT-TEST $(notdir $@)
        @if ! KEYWORD=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/keywords/ -i $@.attrs -f $@.attrs -xx > $@.log 2>&1; then \
                if ! grep ERROR $< 2>&1 > /dev/null; then \
@@ -116,7 +116,7 @@ TESTS.KEYWORDS_FILES := $(addprefix $(BUILD_DIR)/tests/keywords/,$(KEYWORD_FILES
 #
 tests.keywords: $(TESTS.KEYWORDS_FILES)
 
-$(TESTS.KEYWORDS_FILES): $(TESTS.XLAT_FILES)
+$(TESTS.KEYWORDS_FILES): $(TESTS.XLAT_FILES) $(TESTS.MAP_FILES)
 
 .PHONY: clean.tests.keywords
 clean.tests.keywords:
diff --git a/src/tests/keywords/case-empty b/src/tests/keywords/case-empty
new file mode 100644 (file)
index 0000000..46ea054
--- /dev/null
@@ -0,0 +1,23 @@
+# PRE: switch
+#
+update reply {
+       Filter-Id := "filter"
+}
+
+switch &reply:Filter-Id {
+       # deliberately empty
+       case "filter" {
+       }
+
+       case "fail" {
+               update reply {
+                       Filter-Id := "fail"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id := "default"
+               }
+       }
+}
diff --git a/src/tests/keywords/count-error b/src/tests/keywords/count-error
new file mode 100644 (file)
index 0000000..f0723cb
--- /dev/null
@@ -0,0 +1,11 @@
+update control {
+       Cleartext-Password := 'hello'
+}
+
+update reply {
+       Filter-Id := "filter"
+}
+
+update request {
+       Tmp-String-0 := &reply:Filter-Id[#]     # ERROR
+}
diff --git a/src/tests/keywords/else-error b/src/tests/keywords/else-error
new file mode 100644 (file)
index 0000000..3816270
--- /dev/null
@@ -0,0 +1,14 @@
+#
+#  PRE: update if
+#
+#  "else" has to be preceded by an "if" or "elsif"
+#
+if (1) {
+       update reply {
+               Filter-Id := "filter"
+       }
+
+       else {  # ERROR
+            ok
+       }
+}
index 34e7ddf..7645931 100644 (file)
@@ -34,6 +34,7 @@ update request {
        Tmp-Integer-0 = 1
        Tmp-Integer-1 = 3
        Tmp-Integer-2 = 4
+       Tmp-Date-0 = "%l"
 }
 
 if ("%{expr: 1 + 2 * &Tmp-Integer-1 + 4}" != 11) {
@@ -84,18 +85,24 @@ if ("%{expr: 1 << 2 | 1}" != 5) {
        }
 }
 
+if ("%{expr: &Tmp-Date-0}" <= 0) {
+       update reply {
+               Filter-Id := "fail-11"
+       }
+}
+
 #
 #  Unary negation
 #
 if ("%{expr: 6 + -(1 + 3)}" != 2) {
        update reply {
-               Filter-Id := "fail-11"
+               Filter-Id := "fail-12"
        }
 }
 
 if ("%{expr: 6 * -&Tmp-Integer-2}" != -24) {
        update reply {
-               Filter-Id := "fail-11"
+               Filter-Id := "fail-13"
        }
 }
 
index baa54aa..b1f6040 100644 (file)
@@ -38,6 +38,9 @@ foreach control:Tmp-String-0 {
                break
        }
        elsif ("%{Foreach-Variable-0}" == '9') {
+               update reply {
+                       Filter-Id := "fail-9"
+               }
                reject
        }
 }
diff --git a/src/tests/keywords/foreach-break-3 b/src/tests/keywords/foreach-break-3
new file mode 100644 (file)
index 0000000..af03da6
--- /dev/null
@@ -0,0 +1,44 @@
+#
+#  PRE: foreach foreach-break
+#
+
+update request {
+       Calling-Station-Id := "8"
+}
+
+update control {
+       &Calling-Station-Id := "0"
+       &Calling-Station-Id += "1"
+       &Calling-Station-Id += "2"
+       &Calling-Station-Id += "3"
+       &Calling-Station-Id += "4"
+       &Calling-Station-Id += "5"
+       &Calling-Station-Id += "6"
+       &Calling-Station-Id += "7"
+       &Calling-Station-Id += "8"
+       &Calling-Station-Id += "9"
+       &Calling-Station-Id += "a"
+       &Calling-Station-Id += "b"
+       &Calling-Station-Id += "c"
+       &Calling-Station-Id += "d"
+       &Calling-Station-Id += "e"
+       &Calling-Station-Id += "f"
+       &Calling-Station-Id += "g"
+}
+
+foreach &control:Calling-Station-Id {
+       if (&request:Calling-Station-Id == "%{Foreach-Variable-0}") {
+               update reply {
+                       Filter-Id := "filter"
+               }
+
+               break
+       }
+       elsif ("%{Foreach-Variable-0}" == '9') {
+               update reply {
+                       Filter-Id := "fail-9"
+               }
+
+               reject
+       }
+}
diff --git a/src/tests/keywords/foreach-break-4 b/src/tests/keywords/foreach-break-4
new file mode 100644 (file)
index 0000000..037af8e
--- /dev/null
@@ -0,0 +1,44 @@
+#
+#  PRE: foreach foreach-break-3
+#
+
+update request {
+       Calling-Station-Id := "8"
+}
+
+update control {
+       &Calling-Station-Id := "0"
+       &Calling-Station-Id += "1"
+       &Calling-Station-Id += "2"
+       &Calling-Station-Id += "3"
+       &Calling-Station-Id += "4"
+       &Calling-Station-Id += "5"
+       &Calling-Station-Id += "6"
+       &Calling-Station-Id += "7"
+       &Calling-Station-Id += "8"
+       &Calling-Station-Id += "9"
+       &Calling-Station-Id += "a"
+       &Calling-Station-Id += "b"
+       &Calling-Station-Id += "c"
+       &Calling-Station-Id += "d"
+       &Calling-Station-Id += "e"
+       &Calling-Station-Id += "f"
+       &Calling-Station-Id += "g"
+}
+
+foreach &control:Calling-Station-Id {
+       if (&request:Calling-Station-Id == "%{Foreach-Variable-0}") {
+               update reply {
+                       Filter-Id := "filter"
+               }
+
+               break
+       }
+       elsif ("%{Foreach-Variable-0}" == '9') {
+               update reply {
+                       Filter-Id := "fail-9"
+               }
+
+               reject
+       }
+}
index 8c50e8a..f12d6fe 100644 (file)
@@ -89,7 +89,7 @@ if (&request:Tmp-Integer-0[*] > &control:Tmp-Integer-1[*]) {
 }
 
 #
-#  Compiled reqex comparisons
+#  Compiled regex comparisons
 #
 if (&request:Tmp-String-1[*] !~ /PEONS$/) {
        update reply {
@@ -110,7 +110,7 @@ if (&control:Tmp-String-1 =~ /DINKS$/) {
 }
 
 #
-#  Dynamic reqex comparisons
+#  Dynamic regex comparisons
 #
 if (&request:Tmp-String-1[*] !~ /%{Tmp-String-2[0]}$/) {
        update reply {
index a381d28..c9c2d15 100644 (file)
@@ -110,7 +110,10 @@ if (&Tmp-String-0 =~ /^foo$/) {
 }
 
 # compiled - ref - non-multiline
-if (&Tmp-String-0 !~ /^foo\nbar$/) {
+
+# Not all POSIX implementations support the \n character classes
+# so only run this test if the server was built with libpcre.
+if (("${feature.regex-pcre}" == 'yes') && (&Tmp-String-0 !~ /^foo\nbar$/)) {
        update reply {
                Filter-Id += 'Fail 16'
        }
diff --git a/src/tests/keywords/if-regex-multivalue b/src/tests/keywords/if-regex-multivalue
new file mode 100644 (file)
index 0000000..7358c93
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# PRE: update if
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update request {
+       Cisco-AVPair := 'foo=bar'
+       Cisco-AVPair += 'bar=baz'
+       Cisco-AVPair += 'baz=foo'
+}
+
+if (&Cisco-AVPair[*] =~ /bar=(.*)/) {
+       if ("%{1}" != 'baz') {
+               update reply {
+                       Filter-Id += 'Fail 1'
+               }
+       }
+}
+else {
+       update reply {
+               Filter-Id += 'Fail 2'
+       }
+}
diff --git a/src/tests/keywords/map-xlat b/src/tests/keywords/map-xlat
new file mode 100644 (file)
index 0000000..24446a5
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# PRE: update
+#
+#  Test the map xlat
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := "filter"
+}
+
+update {
+       Tmp-String-0 := '&control:Tmp-String-0 := \'testing123\''
+}
+
+if ("%{map:%{Tmp-String-0}}" != 1) {
+    update reply {
+        Filter-Id += 'Fail 0'
+    }
+}
+
+if (&control:Tmp-String-0 != 'testing123') {
+    update reply {
+        Filter-Id += 'Fail 1'
+    }
+}
index a829c6d..4230c57 100644 (file)
@@ -11,25 +11,25 @@ update {
        reply:Filter-Id = 'filter'
 }
 
-if ("%{pairs:request:}" != "User-Name = 'bob', User-Password = 'hello', Tmp-String-0 = 'This is a string', Tmp-String-0 = 'This is another one', Tmp-Octets-0 = 0x000504030201, Tmp-Integer-0 = 7331, Tunnel-Private-Group-Id:5 = '127.0.0.1'") {
+if ("%{pairs:request:}" != "User-Name = \"bob\", User-Password = \"hello\", Tmp-String-0 = \"This is a string\", Tmp-String-0 = \"This is another one\", Tmp-Octets-0 = 0x000504030201, Tmp-Integer-0 = 7331, Tunnel-Private-Group-Id:5 = \"127.0.0.1\"") {
        update reply {
                Filter-Id += 'fail 1'
        }
 }
 
-if ("%{pairs:Tmp-String-0}" != "Tmp-String-0 = 'This is a string'") {
+if ("%{pairs:Tmp-String-0}" != "Tmp-String-0 = \"This is a string\"") {
        update reply {
                Filter-Id += 'fail 2'
        }
 }
 
-if ("%{pairs:Tmp-String-0[*]}" != "Tmp-String-0 = 'This is a string', Tmp-String-0 = 'This is another one'") {
+if ("%{pairs:Tmp-String-0[*]}" != "Tmp-String-0 = \"This is a string\", Tmp-String-0 = \"This is another one\"") {
        update reply {
                Filter-Id += 'fail 3'
        }
 }
 
-if ("%{pairs:control:}" != "Cleartext-Password = 'hello'") {
+if ("%{pairs:control:}" != "Cleartext-Password = \"hello\"") {
        update reply {
                Filter-Id += 'fail 4'
        }
index 58031a4..3b4521a 100644 (file)
@@ -26,6 +26,9 @@ modules {
 
        }
 
+       unix {
+       }
+
        cache {
                driver = "rlm_cache_rbtree"
 
@@ -78,6 +81,27 @@ policy {
                debug_request
                debug_reply
        }
+
+       #
+       #  Just check that this can be referred to as "virtual_policy.post-auth"
+       #
+       virtual_policy {
+               ok
+       }
+
+       with.dots {
+               ok
+       }
+}
+
+instantiate {
+       #
+       #  Just check that this can be referred to as "virtual_instantiate.post-auth"
+       #
+       load-balance virtual_instantiate {
+               ok
+               ok
+       }
 }
 
 server default {
diff --git a/src/tests/keywords/redundant-redundant b/src/tests/keywords/redundant-redundant
new file mode 100644 (file)
index 0000000..294f53e
--- /dev/null
@@ -0,0 +1,73 @@
+# PRE: update if foreach redundant redundant-load-balance
+#
+#  Nested redundant blocks.
+#
+#
+update request {
+       Tmp-Integer-2 := 0
+       Tmp-Integer-3 := 0
+       Tmp-Integer-4 := 0
+       Tmp-Integer-5 := 0
+}
+
+redundant {
+       redundant-load-balance {
+               group {
+                       update request {
+                               Tmp-Integer-2 := "%{expr:&Tmp-Integer-2 + 1}"
+                       }
+                       fail
+               }
+               group {
+                       update request {
+                               Tmp-Integer-3 := "%{expr:&Tmp-Integer-3 + 1}"
+                       }
+                       fail
+               }
+               group {
+                       update request {
+                               Tmp-Integer-4 := "%{expr:&Tmp-Integer-4 + 1}"
+                       }
+                       fail
+               }
+               group {
+                       update request {
+                               Tmp-Integer-5 := "%{expr:&Tmp-Integer-5 + 1}"
+                       }
+                       fail
+               }
+       }
+       ok
+}
+
+if (&Tmp-Integer-2 != 1) {
+       update reply {
+               Filter-Id += 'Fail 3a'
+       }
+       return
+}
+
+if (&Tmp-Integer-3 != 1) {
+       update reply {
+               Filter-Id += 'Fail 3b'
+       }
+       return
+}
+
+if (&Tmp-Integer-4 != 1) {
+       update reply {
+               Filter-Id += 'Fail 3c'
+       }
+       return
+}
+
+if (&Tmp-Integer-5 != 1) {
+       update reply {
+               Filter-Id += 'Fail 3d'
+       }
+       return
+}
+
+update reply {
+       Filter-Id := "filter"
+}
\ No newline at end of file
diff --git a/src/tests/keywords/switch-escape b/src/tests/keywords/switch-escape
new file mode 100644 (file)
index 0000000..50d9fdf
--- /dev/null
@@ -0,0 +1,43 @@
+update request {
+       &Tmp-String-0 := 'foo'
+}
+
+switch "%{tolower:%{request:Tmp-String-0}}" {
+       case 'foo' {
+               update reply {
+                       Filter-Id := "filter"
+               }
+       }
+
+       case '' {
+               update reply {
+                       Filter-Id += "fail-empty-1"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id += "fail-default-1"
+               }
+       }
+}
+
+switch "%{request:Tmp-String-0}" {
+       case 'foo' {
+               update reply {
+                       Filter-Id := "filter"
+               }
+       }
+
+       case '' {
+               update reply {
+                       Filter-Id += "fail-empty-2"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id += "fail-default-2"
+               }
+       }
+}
diff --git a/src/tests/keywords/switch-virtual b/src/tests/keywords/switch-virtual
new file mode 100644 (file)
index 0000000..659604d
--- /dev/null
@@ -0,0 +1,23 @@
+#
+#  PRE: update switch
+#
+update control {
+       Cleartext-Password := 'hello'
+}
+
+#
+#  Virtual attribute references get mashed to xlats
+#
+switch &Packet-Type {
+       case Access-Request {
+               update reply {
+                       Filter-Id := "filter"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id := "fail"
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/tests/keywords/update-delete b/src/tests/keywords/update-delete
new file mode 100644 (file)
index 0000000..a5c2d5a
--- /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'
+       }
+}
+
+# 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'
+       }
+}
+
+# This will of been removed too
+update request {
+       User-Password := 'hello'
+}
diff --git a/src/tests/keywords/update-index b/src/tests/keywords/update-index
new file mode 100644 (file)
index 0000000..390aca7
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# PRE: update update-remove-index
+#
+#  A more generic "update" mechanism
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := "filter"
+}
+
+update {
+       control:Reply-Message += 'a'
+       control:Reply-Message += 'b'
+       control:Reply-Message += 'c'
+}
+
+if ((&control:Reply-Message[0] != 'a') || (&control:Reply-Message[1] != 'b') || (&control:Reply-Message[2] != 'c')) {
+       update {
+               reply:Filter-Id := 'Fail 0'
+       }
+}
+
+# Overwrite a specific index, and check the value here is replaced
+update {
+       &control:Reply-Message[1] := 'd'
+}
+
+if ((&control:Reply-Message[0] != 'a') || (&control:Reply-Message[1] != 'd') || (&control:Reply-Message[2] != 'c')) {
+       update {
+               reply:Filter-Id := 'Fail 1'
+       }
+}
+
+# Check isolation...
+update {
+       &control:Reply-Message[0] := &control:Reply-Message[0]
+}
+
+if ((&control:Reply-Message[0] != 'a') || (&control:Reply-Message[1] != 'd') || (&control:Reply-Message[2] != 'c')) {
+       update {
+               reply:Filter-Id := 'Fail 2'
+       }
+}
+
+# Verify we haven't acquired any extra..
+
+if ("%{control:Reply-Message[#]}" != 3) {
+       update {
+               reply:Filter-Id := 'Fail 3'
+       }
+}
+
diff --git a/src/tests/keywords/virtual b/src/tests/keywords/virtual
new file mode 100644 (file)
index 0000000..d6dbe32
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  PRE: update if
+#
+update control {
+       Cleartext-Password := 'hello'
+}
+
+if (request:Packet-Type == Access-Request) {
+       update reply {
+               Filter-Id := "filter"
+       }
+}
diff --git a/src/tests/keywords/virtual-exists b/src/tests/keywords/virtual-exists
new file mode 100644 (file)
index 0000000..7a8e8f3
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  PRE: update if
+#
+update control {
+       Cleartext-Password := 'hello'
+}
+
+if (&Client-Shortname) {
+       update reply {
+               Filter-Id := "filter"
+       }
+}
diff --git a/src/tests/keywords/virtual-load-balance b/src/tests/keywords/virtual-load-balance
new file mode 100644 (file)
index 0000000..256c2ff
--- /dev/null
@@ -0,0 +1,14 @@
+# PRE: update if foreach
+#
+#  Virtual Load-Balance blocks.
+#
+
+#
+#  Both of these should parse.
+#
+virtual_instantiate
+virtual_instantiate.post-auth
+
+update reply {
+       Filter-Id := 'filter'
+}
diff --git a/src/tests/keywords/virtual-rhs b/src/tests/keywords/virtual-rhs
new file mode 100644 (file)
index 0000000..0d21e7f
--- /dev/null
@@ -0,0 +1,16 @@
+#
+#  PRE: update if
+#
+update control {
+       Cleartext-Password := 'hello'
+}
+
+update request {
+       Tmp-String-0 := "<UNKNOWN-CLIENT>"
+}
+
+if (&Tmp-String-0 == &Client-Shortname) {
+       update reply {
+               Filter-Id := "filter"
+       }
+}
diff --git a/src/tests/keywords/virtual_policy b/src/tests/keywords/virtual_policy
new file mode 100644 (file)
index 0000000..4ab00e2
--- /dev/null
@@ -0,0 +1,15 @@
+# PRE: update if foreach
+#
+#  Virtual policies
+#
+
+
+#
+#  Both of these should parse.
+#
+virtual_policy
+virtual_policy.post-auth
+
+update reply {
+       Filter-Id := 'filter'
+}
diff --git a/src/tests/keywords/with_dots b/src/tests/keywords/with_dots
new file mode 100644 (file)
index 0000000..4fc6b06
--- /dev/null
@@ -0,0 +1,19 @@
+#
+#  PRE: update
+#
+
+#
+#  Ensure that policies can have dots.
+#
+#  The main problem is that conf section references
+#  also have dots in them...
+#
+with.dots
+
+update control {
+       Cleartext-Password := 'hello'
+}
+
+update reply {
+       Filter-Id := "filter"
+}
diff --git a/src/tests/keywords/xlat-list b/src/tests/keywords/xlat-list
new file mode 100644 (file)
index 0000000..fcd9e84
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# PRE: update
+#
+update control {
+    control !* ANY
+}
+
+update control {
+       Tmp-IP-Address-0 := 192.0.2.1
+       Tmp-IP-Address-0 += 192.0.2.2
+}
+
+if ("%{control:[#]}" != 2) {
+       update {
+               reply:Filter-Id += 'fail 0'
+       }
+}
+
+debug_control
+
+if (("%{control:[0]}" != 192.0.2.1) || ("%{control:[1]}" != 192.0.2.2)) {
+       update {
+               reply:Filter-Id += 'fail 1'
+       }
+}
+
+if (("%{control:[n]}" != 192.0.2.2)) {
+    update {
+        reply:Filter-Id += 'fail 1a'
+    }
+}
+
+if ("%{control:[*]}" != '192.0.2.1,192.0.2.2') {
+       update {
+               reply:Filter-Id += 'fail 2'
+       }
+}
+
+# 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 += "%{control:[1]}"
+       Tmp-IP-Address-1 += "%{control:[0]}"
+       Tmp-String-0 = "%{control:[*]}"
+       Tmp-Integer-0 = "%{control:[#]}"
+}
+
+if (Tmp-String-0 != '192.0.2.1,192.0.2.2') {
+       update {
+               reply:Filter-Id += 'fail 3'
+       }
+}
+
+if (Tmp-Integer-0 != 2) {
+       update {
+               reply:Filter-Id += 'fail 4'
+       }
+}
+
+# Boilerplate junk
+update {
+       control:Cleartext-Password := 'hello'
+    reply:Filter-Id := 'filter'
+}
diff --git a/src/tests/map/all.mk b/src/tests/map/all.mk
new file mode 100644 (file)
index 0000000..fedd29d
--- /dev/null
@@ -0,0 +1 @@
+SUBMAKEFILES := map_unit.mk map_tests.mk
diff --git a/src/tests/map/base b/src/tests/map/base
new file mode 100644 (file)
index 0000000..633c32a
--- /dev/null
@@ -0,0 +1,6 @@
+update request {
+       Filter-Id := "filter"
+       User-Name := "blah"
+
+       &reply:Filter-Id += &request:Filter-Id[*]
+}
diff --git a/src/tests/map/base.out b/src/tests/map/base.out
new file mode 100644 (file)
index 0000000..34c519b
--- /dev/null
@@ -0,0 +1,5 @@
+update request {
+       &Filter-Id := "filter"
+       &User-Name := "blah"
+       &reply:Filter-Id += &Filter-Id[*]
+}
diff --git a/src/tests/map/count-error b/src/tests/map/count-error
new file mode 100644 (file)
index 0000000..925360d
--- /dev/null
@@ -0,0 +1,6 @@
+#
+#      This should be an xlat, not a direct assignment
+#
+update request {
+       Tmp-Integer-0 := &Filter-Id[#]  # ERROR
+}
\ No newline at end of file
diff --git a/src/tests/map/count-list-error b/src/tests/map/count-list-error
new file mode 100644 (file)
index 0000000..a7beae1
--- /dev/null
@@ -0,0 +1,6 @@
+#
+#      Updating lists isn't allowed
+#
+update {
+       &request:Filter-Id := &Filter-Id[#]     # ERROR
+}
diff --git a/src/tests/map/map_tests.mk b/src/tests/map/map_tests.mk
new file mode 100644 (file)
index 0000000..886e6b2
--- /dev/null
@@ -0,0 +1,49 @@
+MAP_TESTS      := $(patsubst $(top_srcdir)/src/tests/map/%,%,$(filter-out %.conf %.md %.attrs %.c %.mk %~ %.rej %.out,$(wildcard $(top_srcdir)/src/tests/map/*)))
+MAP_OUTPUT     := $(addsuffix .out,$(addprefix $(BUILD_DIR)/tests/map/,$(MAP_TESTS)))
+MAP_UNIT       := $(BUILD_DIR)/bin/local/map_unit
+
+.PHONY: $(BUILD_DIR)/tests/map/
+$(BUILD_DIR)/tests/map/:
+       @mkdir -p $@
+
+#
+#      Re-run the tests if the test program changes
+#
+#      Create the output directory before the files
+#
+$(MAP_OUTPUT): $(MAP_UNIT) | $(BUILD_DIR)/tests/map/
+
+#
+#      Re-run the tests if the input file changes
+#
+$(BUILD_DIR)/tests/map/%.out: $(top_srcdir)/src/tests/map/%
+       @echo MAP_TEST $(notdir $<)
+       @if ! $(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $< > $@ 2>&1; then \
+               if ! grep ERROR $< 2>&1 > /dev/null; then \
+                       cat $@; \
+                       echo "# $@"; \
+                       echo FAILED: "$(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $<"; \
+                       exit 1; \
+               fi; \
+               FOUND=$$(grep $< $@ | head -1 | sed 's,^.*$(top_srcdir),,;s/:.*//;s/.*\[//;s/\].*//'); \
+               EXPECTED=$$(grep -n ERROR $< | sed 's/:.*//'); \
+               if [ "$$EXPECTED" != "$$FOUND" ]; then \
+                       cat $@; \
+                       echo "# $@"; \
+                       echo "E $$EXPECTED F $$FOUND"; \
+                       echo UNEXPECTED ERROR: "$(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $<"; \
+                       exit 1; \
+               fi; \
+       else \
+               if ! diff $<.out $@; then \
+                       echo FAILED: " diff $<.out $@"; \
+                       echo FAILED: "$(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $<"; \
+                       exit 1; \
+               fi; \
+       fi
+
+TESTS.MAP_FILES := $(MAP_OUTPUT)
+
+$(TESTS.MAP_FILES): $(TESTS.UNIT_FILES)
+
+tests.map: $(MAP_OUTPUT)
diff --git a/src/tests/map/map_unit.c b/src/tests/map/map_unit.c
new file mode 100644 (file)
index 0000000..af6d016
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * radattr.c   Map debugging tool.
+ *
+ * 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 2015  Alan DeKok <aland@freeradius.org>
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+#include <freeradius-devel/conf.h>
+#include <freeradius-devel/modpriv.h>
+#include <freeradius-devel/modcall.h>
+
+#include <ctype.h>
+
+#ifdef HAVE_GETOPT_H
+#      include <getopt.h>
+#endif
+
+#include <assert.h>
+
+#include <freeradius-devel/log.h>
+
+#include <sys/wait.h>
+
+/* Linker hacks */
+
+#ifdef HAVE_PTHREAD_H
+pid_t rad_fork(void)
+{
+       return fork();
+}
+
+pid_t rad_waitpid(pid_t pid, int *status)
+{
+       return waitpid(pid, status, 0);
+}
+#endif
+
+rlm_rcode_t indexed_modcall(UNUSED rlm_components_t comp, UNUSED int idx, UNUSED REQUEST *request)
+{
+       return RLM_MODULE_OK;
+}
+
+char const *get_radius_dir(void)
+{
+       return NULL;
+}
+
+module_instance_t *module_instantiate(UNUSED CONF_SECTION *modules, UNUSED char const *askedname)
+{
+       return NULL;
+}
+
+module_instance_t *module_instantiate_method(UNUSED CONF_SECTION *modules, UNUSED char const *name, UNUSED rlm_components_t *method)
+{
+       return NULL;
+}
+
+/* Linker hacks */
+
+static void NEVER_RETURNS usage(void)
+{
+       fprintf(stderr, "usage: map_unit [OPTS] filename ...\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, "  -O <output_dir>        Set output directory\n");
+       fprintf(stderr, "  -x                     Debugging mode.\n");
+       fprintf(stderr, "  -M                     Show program version information.\n");
+
+       exit(1);
+}
+
+static int process_file(char const *filename)
+{
+       int rcode;
+       char const *name1, *name2;
+       CONF_SECTION *cs, *main_cs;
+       vp_map_t *head, *map;
+       char buffer[8192];
+
+       main_cs = cf_section_alloc(NULL, "main", NULL);
+       if (cf_file_read(main_cs, filename) < 0) {
+               fprintf(stderr, "map_unit: Failed parsing %s\n",
+                       filename);
+               exit(1);
+       }
+
+       /*
+        *      Always has to be an "update" section.
+        */
+       cs = cf_section_sub_find(main_cs, "update");
+       if (!cs) {
+               talloc_free(main_cs);
+               return -1;
+       }
+
+       /*
+        *      Convert the update section to a list of maps.
+        */
+       rcode = map_afrom_cs(&head, cs, PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, modcall_fixup_update, NULL, 128);
+       if (rcode < 0) return -1; /* message already printed */
+       if (!head) {
+               cf_log_err_cs(cs, "'update' sections cannot be empty");
+               return -1;
+       }
+
+       buffer[0] = '\t';
+
+       name1 = cf_section_name1(cs);
+       name2 = cf_section_name2(cs);
+
+       /*
+        *      And print it all out.
+        */
+       if (!name2) {
+               printf("%s {\n", name1);
+       } else {
+               printf("%s %s {\n", name1, name2);
+       }
+
+       for (map = head; map != NULL; map = map->next) {
+               map_prints(buffer + 1, sizeof(buffer) - 1, map);
+               puts(buffer);
+       }
+       printf("}\n");
+
+       talloc_free(main_cs);
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int c, rcode = 0;
+       bool report = false;
+       char const *radius_dir = RADDBDIR;
+       char const *dict_dir = DICTDIR;
+
+       cf_new_escape = true;   /* fix the tests */
+
+#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:xMh")) != EOF) switch (c) {
+               case 'd':
+                       radius_dir = optarg;
+                       break;
+               case 'D':
+                       dict_dir = optarg;
+                       break;
+               case 'x':
+                       fr_debug_lvl++;
+                       rad_debug_lvl = fr_debug_lvl;
+                       break;
+               case 'M':
+                       report = true;
+                       break;
+               case 'h':
+               default:
+                       usage();
+       }
+       argc -= (optind - 1);
+       argv += (optind - 1);
+
+       /*
+        *      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;
+       }
+
+       if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
+               fr_perror("radattr");
+               return 1;
+       }
+
+       if (argc < 2) {
+               rcode = process_file("-");
+
+       } else {
+               rcode = process_file(argv[1]);
+       }
+
+       if (report) {
+               dict_free();
+               fr_log_talloc_report(NULL);
+       }
+
+       if (rcode < 0) rcode = 1; /* internal to Unix process return code */
+
+       return rcode;
+}
diff --git a/src/tests/map/map_unit.mk b/src/tests/map/map_unit.mk
new file mode 100644 (file)
index 0000000..88d319b
--- /dev/null
@@ -0,0 +1,5 @@
+TARGET         := map_unit
+SOURCES                := map_unit.c ${top_srcdir}/src/main/modcall.c
+
+TGT_PREREQS    := libfreeradius-server.a libfreeradius-radius.a
+TGT_LDLIBS     := $(LIBS)
index bcad79b..164509d 100644 (file)
@@ -13,3 +13,6 @@ target used to test the module.  The framework automatically makes the
 tests depend on the module (i.e. library).  So if the module source
 changes, you can just do `make MODULE.test`.  The module will be
 re-built, and the tests will be run.
+
+Note: all SQL tests share the same tests definitions (see sql directory).
+The modules themselves simply link to the actual tests files.
index c2c1547..9960df7 100644 (file)
@@ -32,132 +32,9 @@ $(foreach x,$(TEST_SUBBUILT),$(eval $x.test: rlm_$(subst /,_,$x).la))
 ######################################################################
 #
 #  For the remaining subdirs, add on the directory to include.
+#  test.mk will run the tests for all modules
+#  It is included last so that the module specific makefiles can be processed first
+#  (modules that require a test server can set the corresponding require_test_server variable)
 #
-SUBMAKEFILES := $(addsuffix /all.mk,$(TEST_BUILT) $(subst _,/,$(TEST_SUBBUILT)))
+SUBMAKEFILES := $(addsuffix /all.mk,$(TEST_BUILT) $(subst _,/,$(TEST_SUBBUILT))) test.mk
 
-#
-#  Add the module tests to the overall dependencies
-#
-tests.modules: tests.unit tests.keywords tests.auth $(patsubst %,%.test,$(TEST_BUILT) $(TEST_SUBBUILT))
-
-######################################################################
-#
-#  And now more makefile magic to automatically run the tests
-#  for each module.
-#
-
-define DEFAULT_ATTRS
-ifeq "$(wildcard ${1}.attrs)"
-${1}.attrs
-else
-src/tests/modules/default-input.attrs
-endif
-endef
-
-#
-#  Files in the output dir depend on the unit tests
-#
-#      src/tests/$(MODULE_DIR)/FOO.unlang      unlang for the test
-#      src/tests/$(MODULE_DIR)/FOO.attrs       input RADIUS and output filter
-#      build/tests/$(MODULE_DIR)/FOO.out       updated if the test succeeds
-#      build/tests/$(MODULE_DIR)/FOO.log       debug output for the test
-#
-#  If the test fails, then look for ERROR in the input.  No error
-#  means it's unexpected, so we die.
-#
-#  Otherwise, check the log file for a parse error which matches the
-#  ERROR line in the input.
-#
-$(BUILD_DIR)/tests/modules/%: src/tests/modules/%.unlang $(BUILD_DIR)/tests/modules/%.attrs $(TESTBINDIR)/unittest | build.raddb
-       @mkdir -p $(dir $@)
-       @echo MODULE-TEST $(lastword $(subst /, ,$(dir $@))) $(basename $(notdir $@))
-       @if ! MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx > $@.log 2>&1; then \
-               if ! grep ERROR $< 2>&1 > /dev/null; then \
-                       cat $@.log; \
-                       echo "# $@.log"; \
-                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
-                       exit 1; \
-               fi; \
-               FOUND=$$(grep ^$< $@.log | head -1 | sed 's/:.*//;s/.*\[//;s/\].*//'); \
-               EXPECTED=$$(grep -n ERROR $< | sed 's/:.*//'); \
-               if [ "$$EXPECTED" != "$$FOUND" ]; then \
-                       cat $@.log; \
-                       echo "# $@.log"; \
-                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
-                       exit 1; \
-               fi \
-       fi
-       @touch $@
-
-#
-#  Sometimes we have a default input.  So use that.  Otherwise, use
-#  the input specific to the test.
-#
-MODULE_UNLANG          := $(wildcard src/tests/modules/*/*.unlang src/tests/modules/*/*/*.unlang)
-MODULE_ATTRS_REQUIRES  := $(patsubst %.unlang,%.attrs,$(MODULE_UNLANG))
-MODULE_ATTRS_EXISTS    := $(wildcard src/tests/modules/*/*.attrs src/tests/modules/*/*/*.attrs)
-MODULE_ATTRS_NEEDS     := $(filter-out $(MODULE_ATTRS_EXISTS),$(MODULE_ATTRS_REQUIRES))
-
-MODULE_CONF_REQUIRES   := $(patsubst %.unlang,%.conf,$(MODULE_UNLANG))
-MODULE_CONF_EXISTS     := $(wildcard src/tests/modules/*/*.conf src/tests/modules/*/*/*.attrs)
-MODULE_CONF_NEEDS      := $(filter-out $(MODULE_CONF_EXISTS),$(MODULE_CONF_REQUIRES))
-
-#
-#  The complete list of tests which are to be run
-#
-MODULE_TESTS           := $(patsubst src/tests/modules/%/all.mk,%,$(wildcard src/tests/modules/*/all.mk))
-
-
-#
-#  Target-specific rules
-#
-define MODULE_COPY_FILE
-$(BUILD_DIR)/${1}: src/${1}
-       @mkdir -p $$(@D)
-       @cp $$< $$@
-
-endef
-
-#
-#  Default rules
-#
-define MODULE_COPY_ATTR
-$(BUILD_DIR)/${1}: src/tests/modules/default-input.attrs
-       mkdir -p $$(@D)
-       @cp $$< $$@
-endef
-
-#
-#  FIXME: get this working
-#
-define MODULE_COPY_CONF
-$(BUILD_DIR)/${1}: src/tests/modules/${2}/module.conf
-       @mkdir -p $$(@D)
-       @cp $$< $$@
-endef
-
-define MODULE_FILE_TARGET
-$(BUILD_DIR)/${1}: src/${1}.unlang $(BUILD_DIR)/${1}.attrs
-
-endef
-
-define MODULE_TEST_TARGET
-${1}.test: $(patsubst %.unlang,%,$(subst src,$(BUILD_DIR),$(filter src/tests/modules/${1}/%,$(MODULE_UNLANG))))
-
-endef
-
-#
-#  Create the rules from the list of input files
-#
-$(foreach x,$(MODULE_ATTRS_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
-$(foreach x,$(MODULE_CONF_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
-
-$(foreach x,$(MODULE_ATTRS_NEEDS),$(eval $(call MODULE_COPY_ATTR,$(subst src/,,$x))))
-# FIXME: copy src/tests/modules/*/module.conf to the right place, too
-
-$(foreach x,$(MODULE_UNLANG),$(eval $(call MODULE_FILE_TARGET,$(patsubst %.unlang,%,$(subst src/,,$x)))))
-$(foreach x,$(MODULE_TESTS),$(eval $(call MODULE_TEST_TARGET,$x)))
-
-.PHONY: clean.modules.test
-clean.modules.test:
-       @rm -rf $(BUILD_DIR)/tests/modules/
index 740a390..8f1127f 100644 (file)
@@ -1,7 +1,3 @@
 #
 #  Test the "always" module
 #
-
-#  MODULE.test is the main target for this module.
-always.test:
-       @echo OK: $@
index 96a5d5f..8f89aa6 100644 (file)
@@ -1,2 +1,2 @@
 cache_rbtree.test:
-       @echo OK: cache_rbtree.test
+
index 74619db..07449db 100644 (file)
@@ -1,7 +1,3 @@
 #
 #  Test the "files" module
 #
-
-#  MODULE.test is the main target for this module.
-files.test:
-       @echo OK: files.test
diff --git a/src/tests/modules/ldap/acct.attrs b/src/tests/modules/ldap/acct.attrs
new file mode 100644 (file)
index 0000000..1d57034
--- /dev/null
@@ -0,0 +1,35 @@
+#
+#  Input packet
+#
+User-Name = 'john'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/ldap/acct.unlang b/src/tests/modules/ldap/acct.unlang
new file mode 100644 (file)
index 0000000..2297ea7
--- /dev/null
@@ -0,0 +1,23 @@
+#
+#  Run the "ldap" module
+#  PRE: auth
+#
+ldap.accounting {
+}
+if (ok) {
+        test_pass
+}
+else {
+        test_fail
+}
+
+update {
+        Tmp-String-0 := "%{ldap:ldap://$ENV{TEST_SERVER}/uid=john,ou=people,dc=example,dc=com?description}"
+}
+
+if (&Tmp-String-0 != "User john is online") {
+        test_fail
+}
+else {
+        test_pass
+}
diff --git a/src/tests/modules/ldap/all.mk b/src/tests/modules/ldap/all.mk
new file mode 100644 (file)
index 0000000..3464f68
--- /dev/null
@@ -0,0 +1,11 @@
+#
+#  Test the "ldap" module
+#
+
+#  MODULE.test is the main target for this module.
+
+# Don't test ldap if TEST_SERVER ENV is not set
+ldap_require_test_server := 1
+
+ldap.test:
+       @echo OK: ldap.test
diff --git a/src/tests/modules/ldap/auth.attrs b/src/tests/modules/ldap/auth.attrs
new file mode 100644 (file)
index 0000000..be988ee
--- /dev/null
@@ -0,0 +1,15 @@
+#
+#  Input packet
+#
+User-Name = "john"
+User-Password = "password"
+NAS-IP-Address = 1.2.3.5
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Idle-Timeout == 3600
+Session-Timeout == 7200
+Acct-Interim-Interval == 1800
+Framed-IP-Netmask == "255.255.0.0"
diff --git a/src/tests/modules/ldap/auth.unlang b/src/tests/modules/ldap/auth.unlang
new file mode 100644 (file)
index 0000000..edf14bf
--- /dev/null
@@ -0,0 +1,72 @@
+#
+#  Run the "ldap" module
+#
+ldap
+
+if (&control:NAS-IP-Address != 1.2.3.4) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&control:Reply-Message != "Hello world") {
+        test_fail
+}
+else {
+        test_pass
+}
+
+# Cmp operator means Framed-IP-Address is ignored
+if (&control:Framed-IP-Address) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+# IP netmask defined in profile1 should overwrite radprofile value.
+if (&reply:Framed-IP-Netmask != 255.255.0.0) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&reply:Acct-Interim-Interval != 1800) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&reply:Idle-Timeout != 3600) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if (&reply:Session-Timeout != 7200) {
+        test_fail
+}
+else {
+        test_pass
+}
+
+if ("%{pairs:reply:}" == "") {
+        test_fail
+}
+
+ldap.post-auth
+
+update {
+        Tmp-String-0 := "%{ldap:ldap://$ENV{TEST_SERVER}/uid=john,ou=people,dc=example,dc=com?description}"
+}
+
+if (&Tmp-String-0 != "User %{User-Name} authenticated") {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/ldap/example.com.ldif b/src/tests/modules/ldap/example.com.ldif
new file mode 120000 (symlink)
index 0000000..055d379
--- /dev/null
@@ -0,0 +1 @@
+../../salt-test-server/salt/ldap/base.ldif
\ No newline at end of file
diff --git a/src/tests/modules/ldap/groups_rfc2307bis.attrs b/src/tests/modules/ldap/groups_rfc2307bis.attrs
new file mode 100644 (file)
index 0000000..be988ee
--- /dev/null
@@ -0,0 +1,15 @@
+#
+#  Input packet
+#
+User-Name = "john"
+User-Password = "password"
+NAS-IP-Address = 1.2.3.5
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Idle-Timeout == 3600
+Session-Timeout == 7200
+Acct-Interim-Interval == 1800
+Framed-IP-Netmask == "255.255.0.0"
diff --git a/src/tests/modules/ldap/groups_rfc2307bis.unlang b/src/tests/modules/ldap/groups_rfc2307bis.unlang
new file mode 100644 (file)
index 0000000..b8f48b5
--- /dev/null
@@ -0,0 +1,41 @@
+#
+#  Run the "ldap" module
+#
+ldap
+
+#
+#  Resolve using group name attribute
+#
+if (LDAP-Group == 'foo') {
+        test_pass
+}
+else {
+        test_fail
+}
+
+#
+#  Resolve using group DN
+#
+if (LDAP-Group == 'cn=foo,ou=groups,dc=example,dc=com') {
+        test_pass
+}
+else {
+        test_fail
+}
+
+#
+#  Check we have these values cached
+#
+if (&control:LDAP-Cached-Membership[*] == 'foo') {
+       test_pass
+}
+else {
+       test_fail
+}
+
+if (&control:LDAP-Cached-Membership[*] == 'cn=foo,ou=groups,dc=example,dc=com') {
+       test_pass
+}
+else {
+       test_fail
+}
diff --git a/src/tests/modules/ldap/module.conf b/src/tests/modules/ldap/module.conf
new file mode 100644 (file)
index 0000000..3e5f128
--- /dev/null
@@ -0,0 +1,537 @@
+# -*- text -*-
+#
+#  $Id$
+
+#
+#  Lightweight Directory Access Protocol (LDAP)
+#
+ldap {
+       #  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.
+       #
+       #  Depending on the libldap in use, server may be an LDAP URI.
+       #  In the case of OpenLDAP this allows additional the following
+       #  additional schemes:
+       #  - ldaps:// (LDAP over SSL)
+       #  - ldapi:// (LDAP over Unix socket)
+       #  - ldapc:// (Connectionless LDAP)
+       server = $ENV{LDAP_TEST_SERVER}
+#      server = 'ldap.rrdns.example.org'
+
+       #  Port to connect on, defaults to 389, will be ignored for LDAP URIs.
+#      port = 389
+
+       #  Administrator account for searching and possibly modifying.
+       identity = 'cn=admin,dc=example,dc=com'
+       password = secret
+
+       #  Unless overridden in another section, the dn from which all
+       #  searches will start from.
+       base_dn = 'dc=example,dc=com'
+
+       #  SASL parameters to use for admin binds
+       #
+       #  When we're prompted by the SASL library, these control
+       #  the responses given.
+       #
+       sasl {
+               # SASL mechanism
+#              mech = 'PLAIN'
+
+               # SASL authorisation identity to proxy.
+#              proxy = 'autz_id'
+
+               # SASL realm. Used for kerberos.
+#              realm = 'example.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 operator (=, :=, +=, -=).
+       #       <value>:        Is the value to parse into the new valuepair.
+       #                       If the value 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.
+       #
+       #  Configuration items are in the format:
+       #       <radius attr> <op> <ldap attr>
+       #
+       #  Where:
+       #       <radius attr>:  Is the destination RADIUS attribute
+       #                       with any valid list and request qualifiers.
+       #       <op>:           Is any assignment attribute (=, :=, +=, -=).
+       #       <ldap attr>:    Is the attribute associated with user or
+       #                       profile objects in the LDAP directory.
+       #                       If the attribute name is wrapped in double
+       #                       quotes it will be xlat expanded.
+       #
+       #  Request and list qualifiers may also be placed after the 'update'
+       #  section name to set defaults destination requests/lists
+       #  for unqualified RADIUS attributes.
+       #
+       #  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'
+               reply:Idle-Timeout              := 'radiusIdleTimeout'
+               reply:Framed-IP-Netmask         := 'radiusFramedIPNetmask'
+#              control:NT-Password             := 'ntPassword'
+#              reply:Reply-Message             := 'radiusReplyMessage'
+#              reply:Tunnel-Type               := 'radiusTunnelType'
+#              reply:Tunnel-Medium-Type        := 'radiusTunnelMediumType'
+#              reply:Tunnel-Private-Group-ID   := 'radiusTunnelPrivategroupId'
+
+               #  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:                        += 'radiusControlAttribute'
+               request:                        += 'radiusRequestAttribute'
+               reply:                          += 'radiusReplyAttribute'
+       }
+
+       #  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.
+#      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
+               base_dn = "ou=people,${..base_dn}"
+
+               #  Filter for user objects, should be specific enough
+               #  to identify a single user object.
+               filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
+
+               #  SASL parameters to use for user binds
+               #
+               #  When we're prompted by the SASL library, these control
+               #  the responses given.
+               #
+               #  Any of the config items below may be an attribute ref
+               #  or and expansion, so different SASL mechs, proxy IDs
+               #  and realms may be used for different users.
+               sasl {
+                       # SASL mechanism
+#                      mech = 'PLAIN'
+
+                       # SASL authorisation identity to proxy.
+#                      proxy = &User-Name
+
+                       # SASL realm. Used for kerberos.
+#                      realm = 'example.org'
+               }
+
+               #  Search scope, may be 'base', 'one', sub' or 'children'
+#              scope = 'sub'
+
+               #  If this is undefined, anyone is authorised.
+               #  If it is defined, the contents of this attribute
+               #  determine whether or not the user is authorised
+#              access_attribute = 'dialupAccess'
+
+               #  Control whether the presence of 'access_attribute'
+               #  allows access, or denys access.
+               #
+               #  If 'yes', and the access_attribute is present, or
+               #  'no' and the access_attribute is absent then access
+               #  will be allowed.
+               #
+               #  If 'yes', and the access_attribute is absent, or
+               #  'no' and the access_attribute is present, then
+               #  access will not be allowed.
+               #
+               #  If the value of the access_attribute is 'false', it
+               #  will negate the result.
+               #
+               #  e.g.
+               #    access_positive = yes
+               #    access_attribute = userAccessAllowed
+               #
+               #  With an LDAP object containing:
+               #    userAccessAllowed: false
+               #
+               #  Will result in the user being locked out.
+#              access_positive = yes
+       }
+
+       #
+       #  User membership checking.
+       #
+       group {
+               #  Where to start searching in the tree for groups
+               base_dn = "ou=groups,${..base_dn}"
+
+               #  Filter for group objects, should match all available
+               #  group objects a user might be a member of.
+               filter = '(objectClass=groupOfNames)'
+
+               # Search scope, may be 'base', 'one', sub' or 'children'
+               scope = 'sub'
+
+               #  Attribute that uniquely identifies a group.
+               #  Is used when converting group DNs to group
+               #  names.
+               name_attribute = cn
+
+               #  Filter to find group objects a user is a member of.
+               #  That is, group objects with attributes that
+               #  identify members (the inverse of membership_attribute).
+               membership_filter = "(|(member=%{control:Ldap-UserDn})(memberUid=%{%{Stripped-User-Name}:-%{User-Name}}))"
+
+               #  The attribute in user objects which contain the names
+               #  or DNs of groups a user is a member of.
+               #
+               #  Unless a conversion between group name and group DN is
+               #  needed, there's no requirement for the group objects
+               #  referenced to actually exist.
+               membership_attribute = 'memberOf'
+
+               #  If cacheable_name or cacheable_dn are enabled,
+               #  all group information for the user will be
+               #  retrieved from the directory and written to LDAP-Group
+               #  attributes appropriate for the instance of rlm_ldap.
+               #
+               #  For group comparisons these attributes will be checked
+               #  instead of querying the LDAP directory directly.
+               #
+               #  This feature is intended to be used with rlm_cache.
+               #
+               #  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 = yes
+               cacheable_dn = yes
+
+               #  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'
+       }
+
+       #
+       #  User profiles. RADIUS profile objects contain sets of attributes
+       #  to insert into the request. These attributes are mapped using
+       #  the same mapping scheme applied to user objects.
+       #
+       profile {
+               #  Filter for RADIUS profile objects
+               filter = '(objectclass=radiusprofile)'
+
+               #  The default profile applied to all users.
+               default = 'cn=radprofile,ou=profiles,dc=example,dc=com'
+
+               #  The list of profiles which are applied (after the default)
+               #  to all users.
+               #  The 'User-Profile' attribute in the control list
+               #  will override this setting at run-time.
+               attribute = 'radiusProfileDn'
+       }
+
+       #
+       #  Bulk load clients from the directory
+       #
+       client {
+               #   Where to start searching in the tree for clients
+               base_dn = "ou=clients,${..base_dn}"
+
+               #
+               #  Filter to match client objects
+               #
+               filter = '(objectClass=radiusClient)'
+
+               # Search scope, may be 'base', 'one', 'sub' or 'children'
+#              scope = 'sub'
+
+               #
+               #  Sets default values (not obtained from LDAP) for new client entries
+               #
+               template {
+#                      login                           = 'test'
+#                      password                        = 'test'
+#                      proto                           = tcp
+#                      require_message_authenticator   = yes
+
+                       # Uncomment to add a home_server with the same
+                       # attributes as the client.
+#                      coa_server {
+#                              response_window = 2.0
+#                      }
+               }
+
+               #
+               #  Client attribute mappings are in the format:
+               #      <client attribute> = <ldap attribute>
+               #
+               #  The following attributes are required:
+               #    * ipaddr | ipv4addr | ipv6addr - Client IP Address.
+               #    * secret - RADIUS shared secret.
+               #
+               #  All other attributes usually supported in a client
+               #  definition are also supported here.
+               #
+               #  Schemas are available in doc/schemas/ldap for openldap and eDirectory
+               #
+               attribute {
+                       ipaddr                          = '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.
+       #
+       #  LDAP modification items are in the format:
+       #       <ldap attr> <op> <value>
+       #
+       #  Where:
+       #       <ldap attr>:    The LDAP attribute to add modify or delete.
+       #       <op>:           One of the assignment operators:
+       #                       (:=, +=, -=, ++).
+       #                       Note: '=' is *not* supported.
+       #       <value>:        The value to add modify or delete.
+       #
+       #  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}}"
+
+               type {
+                       start {
+                               update {
+                                       description := "User %{User-Name} is online"
+                               }
+                       }
+
+                       interim-update {
+                               update {
+                                       description := "Last seen at %S"
+                               }
+                       }
+
+                       stop {
+                               update {
+                                       description := "Offline at %S"
+                               }
+                       }
+               }
+       }
+
+       #
+       #  Post-Auth can modify LDAP objects too
+       #
+       post-auth {
+               update {
+                       description := "User %{User-Name} authenticated"
+               }
+       }
+
+       #
+       #  LDAP connection-specific options.
+       #
+       #  These options set timeouts, keep-alives, etc. for the connections.
+       #
+       options {
+               #  Control under which situations aliases are followed.
+               #  May be one of 'never', 'searching', 'finding' or 'always'
+               #  default: libldap's default which is usually 'never'.
+               #
+               #  LDAP_OPT_DEREF is set to this value.
+#              dereference = 'always'
+
+               #
+               #  The following two configuration items control whether the
+               #  server follows references returned by LDAP directory.
+               #  They are  mostly for Active Directory compatibility.
+               #  If you set these to 'no', then searches will likely return
+               #  'operations error', instead of a useful result.
+               #
+               chase_referrals = yes
+               rebind = yes
+
+               #  Seconds to wait for LDAP query to finish. default: 20
+               timeout = 10
+
+               #  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
+               #
+               #  LDAP_OPT_NETWORK_TIMEOUT is set to this value.
+               net_timeout = 1
+
+               #  LDAP_OPT_X_KEEPALIVE_IDLE
+               idle = 60
+
+               #  LDAP_OPT_X_KEEPALIVE_PROBES
+               probes = 3
+
+               #  LDAP_OPT_X_KEEPALIVE_INTERVAL
+               interval = 3
+
+               #  ldap_debug: debug flag for LDAP SDK
+               #  (see OpenLDAP documentation).  Set this to enable
+               #  huge amounts of LDAP debugging on the screen.
+               #  You should only use this if you are an LDAP expert.
+               #
+               #       default: 0x0000 (no debugging messages)
+               #       Example:(LDAP_DEBUG_FILTER+LDAP_DEBUG_CONNS)
+               ldap_debug = 0x0028
+       }
+
+       #
+       #  This subsection configures the tls related items
+       #  that control how FreeRADIUS connects to an LDAP
+       #  server.  It contains all of the 'tls_*' configuration
+       #  entries used in older versions of FreeRADIUS.  Those
+       #  configuration entries can still be used, but we recommend
+       #  using these.
+       #
+       tls {
+               # Set this to 'yes' to use TLS encrypted connections
+               # to the LDAP database by using the StartTLS extended
+               # operation.
+               #
+               # The StartTLS operation is supposed to be
+               # used with normal ldap connections instead of
+               # using ldaps (port 636) connections
+#              start_tls = yes
+
+#              ca_file = ${certdir}/cacert.pem
+
+#              ca_path = ${certdir}
+#              certificate_file = /path/to/radius.crt
+#              private_key_file = /path/to/radius.key
+#              random_file = ${certdir}/random
+
+               #  Certificate Verification requirements.  Can be:
+               #    'never' (don't even bother trying)
+               #    'allow' (try, but don't fail if the certificate
+               #               can't be verified)
+               #    'demand' (fail if the certificate doesn't verify.)
+               #
+               #  The default is 'allow'
+#              require_cert    = 'demand'
+       }
+
+
+       #  As of version 3.0, the 'pool' section has replaced the
+       #  following configuration items:
+       #
+       #  ldap_connections_number
+
+       #  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
+
+               #  Minimum number of connections to keep open
+               min = 4
+
+               #  Maximum number of connections
+               #
+               #  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 = 4
+
+               #  Spare connections to be left idle
+               #
+               #  NOTE: Idle connections WILL be closed if 'idle_timeout'
+               #  is set.
+               spare = 3
+
+               #  Number of uses before the connection is closed
+               #
+               #  0 means 'infinite'
+               uses = 0
+
+               #  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 = 60
+
+               # The number of seconds to wait after the server tries
+               # to open a connection, and fails.  During this time,
+               # no new connections will be opened.
+               #
+               retry_delay = 1
+
+               #  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/tests/modules/preprocess/all.mk b/src/tests/modules/preprocess/all.mk
new file mode 100644 (file)
index 0000000..5cfad60
--- /dev/null
@@ -0,0 +1,3 @@
+#
+#  Test the "preprocess" module
+#
diff --git a/src/tests/modules/preprocess/hints b/src/tests/modules/preprocess/hints
new file mode 100644 (file)
index 0000000..14ceafc
--- /dev/null
@@ -0,0 +1,2 @@
+DEFAULT
+       Calling-Station-Id := "%{User-Name}@%{NAS-IP-Address}"
diff --git a/src/tests/modules/preprocess/huntgroups b/src/tests/modules/preprocess/huntgroups
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/tests/modules/preprocess/module.conf b/src/tests/modules/preprocess/module.conf
new file mode 100644 (file)
index 0000000..7c51fa6
--- /dev/null
@@ -0,0 +1,4 @@
+preprocess {
+       hints = $ENV{MODULE_TEST_DIR}/hints
+       huntgroups = $ENV{MODULE_TEST_DIR}/huntgroups
+}
diff --git a/src/tests/modules/preprocess/xlat.attrs b/src/tests/modules/preprocess/xlat.attrs
new file mode 100644 (file)
index 0000000..e7170d1
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  Input packet
+#
+User-Name = "bob"
+User-Password = "bob"
+NAS-IP-Address = 127.0.0.1
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Filter-Id == 'success'
diff --git a/src/tests/modules/preprocess/xlat.unlang b/src/tests/modules/preprocess/xlat.unlang
new file mode 100644 (file)
index 0000000..da53982
--- /dev/null
@@ -0,0 +1,14 @@
+#
+#  Run the preprocess module
+#
+preprocess
+
+if (Calling-Station-Id == "bob@127.0.0.1") {
+       update reply {
+               Filter-Id := "success"
+       }
+}
+
+update control {
+       Cleartext-Password := "%{User-Name}"
+}
index 37d6155..220f341 100644 (file)
@@ -54,4 +54,50 @@ policy {
                }
                reject
        }
+
+       #
+       #  Outputs the contents of the control list in debugging (-X) mode
+       #
+       debug_control {
+               if("%{debug_attr:control:}" == '') {
+                       noop
+               }
+       }
+
+       #
+       #  Outputs the contents of the request list in debugging (-X) mode
+       #
+       debug_request {
+               if("%{debug_attr:request:}" == '') {
+                       noop
+               }
+       }
+
+       #
+       #  Outputs the contents of the reply list in debugging (-X) mode
+       #
+       debug_reply {
+               if("%{debug_attr:reply:}" == '') {
+                       noop
+               }
+       }
+
+       #
+       #  Outputs the contents of the session state list in debugging (-X) mode
+       #
+       debug_session_state {
+               if("%{debug_attr:session-state:}" == '') {
+                       noop
+               }
+       }
+
+       #
+       #  Outputs the contents of the main lists in debugging (-X) mode
+       #
+       debug_all {
+               debug_control
+               debug_request
+               debug_reply
+               debug_session_state
+       }
 }
diff --git a/src/tests/modules/sql/.gitignore b/src/tests/modules/sql/.gitignore
new file mode 100644 (file)
index 0000000..405551a
--- /dev/null
@@ -0,0 +1 @@
+rlm_sql_sqlite.db
diff --git a/src/tests/modules/sql/README b/src/tests/modules/sql/README
new file mode 100644 (file)
index 0000000..b5e79e2
--- /dev/null
@@ -0,0 +1,7 @@
+All SQL related tests should be defined here and the driver specific directory need only have links to the tests defined here.
+
+All User-Name attributes, Acct-Session-Id, and Acct-Multi-Session-Id attributes
+
+       MUST BE UNIQUE FOR EVERY TEST.
+
+Otherwise the tests will stomp on each other when run in parallel.
diff --git a/src/tests/modules/sql/acct_0_start.attrs b/src/tests/modules/sql/acct_0_start.attrs
new file mode 100644 (file)
index 0000000..01257ce
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user0@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000000'
+Acct-Unique-Session-Id = '00000000'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql/acct_0_start.unlang b/src/tests/modules/sql/acct_0_start.unlang
new file mode 100644 (file)
index 0000000..64921b1
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql:DELETE FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000000'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql/acct_1_update.attrs b/src/tests/modules/sql/acct_1_update.attrs
new file mode 100644 (file)
index 0000000..28db958
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user1@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Interim-Update
+Acct-Delay-Time = 1
+Acct-Input-Octets = 10
+Acct-Output-Octets = 10
+Acct-Session-Id = '00000001'
+Acct-Unique-Session-Id = '00000001'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 30
+Acct-Input-Packets = 10
+Acct-Output-Packets = 10
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql/acct_1_update.unlang b/src/tests/modules/sql/acct_1_update.unlang
new file mode 100644 (file)
index 0000000..e566a4a
--- /dev/null
@@ -0,0 +1,30 @@
+#
+#  PRE: acct_0_start
+#
+sql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000001'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql/acct_2_stop.attrs b/src/tests/modules/sql/acct_2_stop.attrs
new file mode 100644 (file)
index 0000000..e932f84
--- /dev/null
@@ -0,0 +1,38 @@
+#
+#  Input packet
+#
+User-Name = 'user2@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Stop
+Acct-Terminate-Cause = User-Request
+Acct-Delay-Time = 1
+Acct-Input-Octets = 15
+Acct-Output-Octets = 15
+Acct-Session-Id = '00000002'
+Acct-Unique-Session-Id = '00000002'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 120
+Acct-Input-Packets = 15
+Acct-Output-Packets = 15
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql/acct_2_stop.unlang b/src/tests/modules/sql/acct_2_stop.unlang
new file mode 100644 (file)
index 0000000..3386c71
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  PRE: acct_1_update
+#
+sql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 120)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-String-0 := "%{sql:SELECT AcctTerminateCause FROM radacct WHERE AcctSessionId = '00000002'}"
+}
+if (!&Tmp-String-0 || (&Tmp-String-0 != 'User-Request')) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql/acct_start_conflict.attrs b/src/tests/modules/sql/acct_start_conflict.attrs
new file mode 100644 (file)
index 0000000..2bcade3
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user3@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Start
+Acct-Delay-Time = 1
+Acct-Input-Octets = 0
+Acct-Output-Octets = 0
+Acct-Session-Id = '00000003'
+Acct-Unique-Session-Id = '00000003'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 0
+Acct-Input-Packets = 0
+Acct-Output-Packets = 0
+Acct-Input-Gigawords = 0
+Acct-Output-Gigawords = 0
+Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql/acct_start_conflict.unlang b/src/tests/modules/sql/acct_start_conflict.unlang
new file mode 100644 (file)
index 0000000..65e69e0
--- /dev/null
@@ -0,0 +1,76 @@
+#
+#  Check that conflicting unique IDs triggers failover to alternative query
+#
+
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql:DELETE FROM radacct WHERE AcctSessionId = '00000003'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Insert the Accounting-Request start
+#
+sql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+#
+#  Check the database has at least one row
+#
+update {
+       Tmp-Integer-0 := "%{sql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000003'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Check acctsessiontime matches the value in the request
+#
+update {
+       Tmp-Integer-0 := "%{sql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000003'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+#
+#  Change acctsessiontime and verify it's updated
+#
+update request {
+       Connect-Info = 'updated'
+}
+sql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+update {
+       Tmp-String-0 := "%{sql:SELECT connectinfo_start FROM radacct WHERE AcctSessionId = '00000003'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-String-0 != 'updated')) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql/acct_update_no_start.attrs b/src/tests/modules/sql/acct_update_no_start.attrs
new file mode 100644 (file)
index 0000000..6f3049e
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Input packet
+#
+User-Name = 'user4@example.org'
+NAS-Port = 17826193
+NAS-IP-Address = 192.0.2.10
+Framed-IP-Address = 198.51.100.59
+NAS-Identifier = 'nas.example.org'
+Acct-Status-Type = Interim-Update
+Acct-Delay-Time = 1
+Acct-Input-Octets = 10
+Acct-Output-Octets = 10
+Acct-Session-Id = '00000004'
+Acct-Unique-Session-Id = '00000004'
+Acct-Authentic = RADIUS
+Acct-Session-Time = 30
+Acct-Input-Packets = 10
+Acct-Output-Packets = 10
+Acct-Input-Gigawords = 1
+Acct-Output-Gigawords = 1
+Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
+NAS-Port-Type = Ethernet
+NAS-Port-Id = 'port 001'
+Service-Type = Framed-User
+Framed-Protocol = PPP
+Acct-Link-Count = 0
+Idle-Timeout = 0
+Session-Timeout = 604800
+Access-Loop-Encapsulation = 0x000000
+Proxy-State = 0x323531
+
+#
+#  Expected answer
+#
+#  There's not an Accounting-Failed packet type in RADIUS...
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/sql/acct_update_no_start.unlang b/src/tests/modules/sql/acct_update_no_start.unlang
new file mode 100644 (file)
index 0000000..3875b2d
--- /dev/null
@@ -0,0 +1,40 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql:DELETE FROM radacct WHERE AcctSessionId = '00000004'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+sql.accounting
+if (ok) {
+       test_pass
+}
+else {
+       test_fail
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT count(*) FROM radacct WHERE AcctSessionId = '00000004'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
+       test_fail
+}
+else {
+       test_pass
+}
+
+update {
+       Tmp-Integer-0 := "%{sql:SELECT acctsessiontime FROM radacct WHERE AcctSessionId = '00000004'}"
+}
+if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
+       test_fail
+}
+else {
+       test_pass
+}
diff --git a/src/tests/modules/sql/auth.attrs b/src/tests/modules/sql/auth.attrs
new file mode 100644 (file)
index 0000000..e7d1498
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  Input packet
+#
+User-Name = "user_auth"
+User-Password = "password"
+NAS-IP-Address = "1.2.3.4"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Idle-Timeout == 3600
diff --git a/src/tests/modules/sql/auth.unlang b/src/tests/modules/sql/auth.unlang
new file mode 100644 (file)
index 0000000..0d76538
--- /dev/null
@@ -0,0 +1,39 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql:DELETE FROM radcheck WHERE username = 'user_auth'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:INSERT INTO radcheck (username, attribute, op, value) VALUES ('user_auth', 'NAS-IP-Address', '==', '1.2.3.4')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:INSERT INTO radcheck (username, attribute, op, value) VALUES ('user_auth', 'Cleartext-Password', ':=', 'password')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:DELETE FROM radreply WHERE username = 'user_auth'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:INSERT INTO radreply (username, attribute, op, value) VALUES ('user_auth', 'Idle-Timeout', ':=', '3600')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+sql
diff --git a/src/tests/modules/sql/reject.attrs b/src/tests/modules/sql/reject.attrs
new file mode 100644 (file)
index 0000000..cb0b9a2
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  Input packet
+#
+User-Name = "user_reject"
+User-Password = "password"
+NAS-IP-Address = "1.2.3.4"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Reject
+Reply-Message == "Authentication failed"
diff --git a/src/tests/modules/sql/reject.unlang b/src/tests/modules/sql/reject.unlang
new file mode 100644 (file)
index 0000000..b4afb09
--- /dev/null
@@ -0,0 +1,39 @@
+#
+#  Clear out old data
+#
+update {
+       Tmp-String-0 := "%{sql:DELETE FROM radcheck WHERE username = 'user_reject'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:INSERT INTO radcheck (username, attribute, op, value) VALUES ('user_reject', 'NAS-IP-Address', '==', '1.2.3.4')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:INSERT INTO radcheck (username, attribute, op, value) VALUES ('user_reject', 'Cleartext-Password', ':=', 'wrong-password')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:DELETE FROM radreply WHERE username = 'user_reject'}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+update {
+       Tmp-String-0 := "%{sql:INSERT INTO radreply (username, attribute, op, value) VALUES ('user_reject', 'Reply-Message', ':=', 'Authentication failed')}"
+}
+if (!&Tmp-String-0) {
+       test_fail
+}
+
+sql
diff --git a/src/tests/modules/sql_mysql/.gitignore b/src/tests/modules/sql_mysql/.gitignore
new file mode 100644 (file)
index 0000000..405551a
--- /dev/null
@@ -0,0 +1 @@
+rlm_sql_sqlite.db
diff --git a/src/tests/modules/sql_mysql/acct_0_start.attrs b/src/tests/modules/sql_mysql/acct_0_start.attrs
new file mode 120000 (symlink)
index 0000000..24e17ae
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_0_start.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_0_start.unlang b/src/tests/modules/sql_mysql/acct_0_start.unlang
new file mode 120000 (symlink)
index 0000000..3fe3e99
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_0_start.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_1_update.attrs b/src/tests/modules/sql_mysql/acct_1_update.attrs
new file mode 120000 (symlink)
index 0000000..1ab772d
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_1_update.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_1_update.unlang b/src/tests/modules/sql_mysql/acct_1_update.unlang
new file mode 120000 (symlink)
index 0000000..b69ff9b
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_1_update.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_2_stop.attrs b/src/tests/modules/sql_mysql/acct_2_stop.attrs
new file mode 120000 (symlink)
index 0000000..ea73931
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_2_stop.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_2_stop.unlang b/src/tests/modules/sql_mysql/acct_2_stop.unlang
new file mode 120000 (symlink)
index 0000000..ea0be56
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_2_stop.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_start_conflict.attrs b/src/tests/modules/sql_mysql/acct_start_conflict.attrs
new file mode 120000 (symlink)
index 0000000..117a505
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_start_conflict.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_start_conflict.unlang b/src/tests/modules/sql_mysql/acct_start_conflict.unlang
new file mode 120000 (symlink)
index 0000000..da35798
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_start_conflict.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_update_no_start.attrs b/src/tests/modules/sql_mysql/acct_update_no_start.attrs
new file mode 120000 (symlink)
index 0000000..328867f
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_update_no_start.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/acct_update_no_start.unlang b/src/tests/modules/sql_mysql/acct_update_no_start.unlang
new file mode 120000 (symlink)
index 0000000..6837977
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_update_no_start.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/all.mk b/src/tests/modules/sql_mysql/all.mk
new file mode 100644 (file)
index 0000000..337528b
--- /dev/null
@@ -0,0 +1,6 @@
+#
+#  Test the mysql module
+#
+
+# Don't test sql_mysql if TEST_SERVER ENV is not set
+sql_mysql_require_test_server := 1
diff --git a/src/tests/modules/sql_mysql/auth.attrs b/src/tests/modules/sql_mysql/auth.attrs
new file mode 120000 (symlink)
index 0000000..6b30b6b
--- /dev/null
@@ -0,0 +1 @@
+../sql/auth.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/auth.unlang b/src/tests/modules/sql_mysql/auth.unlang
new file mode 120000 (symlink)
index 0000000..3ccd80e
--- /dev/null
@@ -0,0 +1 @@
+../sql/auth.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/module.conf b/src/tests/modules/sql_mysql/module.conf
new file mode 100644 (file)
index 0000000..e3aa02d
--- /dev/null
@@ -0,0 +1,53 @@
+sql {
+       driver = "rlm_sql_mysql"
+       dialect = "mysql"
+
+        # Connection info:
+        #
+        server = $ENV{SQL_MYSQL_TEST_SERVER}
+        port = 3306
+        login = "radius"
+        password = "radpass"
+
+        # Database table configuration for everything except Oracle
+        radius_db = "radius"
+       radius_db = "radius"
+
+       acct_table1 = "radacct"
+       acct_table2 = "radacct"
+       postauth_table = "radpostauth"
+       authcheck_table = "radcheck"
+       groupcheck_table = "radgroupcheck"
+       authreply_table = "radreply"
+       groupreply_table = "radgroupreply"
+       usergroup_table = "radusergroup"
+       read_groups = yes
+       read_profiles = yes
+
+       # Remove stale session if checkrad does not see a double login
+       delete_stale_sessions = yes
+
+       pool {
+               start = 1
+               min = 0
+               max = 1
+               spare = 3
+               uses = 2
+               lifetime = 1
+               idle_timeout = 60
+               retry_delay = 1
+       }
+
+       # Set to 'yes' to read radius clients from the database ('nas' table)
+       # Clients will ONLY be read on server startup.
+#      read_clients = yes
+
+       # Table to keep radius client info
+       client_table = "nas"
+
+       # The group attribute specific to this instance of rlm_sql
+       group_attribute = "SQL-Group"
+
+       # Read database-specific queries
+       $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
+}
diff --git a/src/tests/modules/sql_mysql/reject.attrs b/src/tests/modules/sql_mysql/reject.attrs
new file mode 120000 (symlink)
index 0000000..71a187f
--- /dev/null
@@ -0,0 +1 @@
+../sql/reject.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_mysql/reject.unlang b/src/tests/modules/sql_mysql/reject.unlang
new file mode 120000 (symlink)
index 0000000..379839f
--- /dev/null
@@ -0,0 +1 @@
+../sql/reject.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/.gitignore b/src/tests/modules/sql_postgresql/.gitignore
new file mode 100644 (file)
index 0000000..405551a
--- /dev/null
@@ -0,0 +1 @@
+rlm_sql_sqlite.db
diff --git a/src/tests/modules/sql_postgresql/acct_0_start.attrs b/src/tests/modules/sql_postgresql/acct_0_start.attrs
new file mode 120000 (symlink)
index 0000000..24e17ae
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_0_start.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_0_start.unlang b/src/tests/modules/sql_postgresql/acct_0_start.unlang
new file mode 120000 (symlink)
index 0000000..3fe3e99
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_0_start.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_1_update.attrs b/src/tests/modules/sql_postgresql/acct_1_update.attrs
new file mode 120000 (symlink)
index 0000000..1ab772d
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_1_update.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_1_update.unlang b/src/tests/modules/sql_postgresql/acct_1_update.unlang
new file mode 120000 (symlink)
index 0000000..b69ff9b
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_1_update.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_2_stop.attrs b/src/tests/modules/sql_postgresql/acct_2_stop.attrs
new file mode 120000 (symlink)
index 0000000..ea73931
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_2_stop.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_2_stop.unlang b/src/tests/modules/sql_postgresql/acct_2_stop.unlang
new file mode 120000 (symlink)
index 0000000..ea0be56
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_2_stop.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_start_conflict.attrs b/src/tests/modules/sql_postgresql/acct_start_conflict.attrs
new file mode 120000 (symlink)
index 0000000..117a505
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_start_conflict.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_start_conflict.unlang b/src/tests/modules/sql_postgresql/acct_start_conflict.unlang
new file mode 120000 (symlink)
index 0000000..da35798
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_start_conflict.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_update_no_start.attrs b/src/tests/modules/sql_postgresql/acct_update_no_start.attrs
new file mode 120000 (symlink)
index 0000000..328867f
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_update_no_start.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/acct_update_no_start.unlang b/src/tests/modules/sql_postgresql/acct_update_no_start.unlang
new file mode 120000 (symlink)
index 0000000..6837977
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_update_no_start.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/all.mk b/src/tests/modules/sql_postgresql/all.mk
new file mode 100644 (file)
index 0000000..efd20d9
--- /dev/null
@@ -0,0 +1,6 @@
+#
+#  Test the postgresql module
+#
+
+# Don't test sql_postgresql if TEST_SERVER ENV is not set
+sql_postgresql_require_test_server := 1
diff --git a/src/tests/modules/sql_postgresql/auth.attrs b/src/tests/modules/sql_postgresql/auth.attrs
new file mode 120000 (symlink)
index 0000000..6b30b6b
--- /dev/null
@@ -0,0 +1 @@
+../sql/auth.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/auth.unlang b/src/tests/modules/sql_postgresql/auth.unlang
new file mode 120000 (symlink)
index 0000000..3ccd80e
--- /dev/null
@@ -0,0 +1 @@
+../sql/auth.unlang
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/module.conf b/src/tests/modules/sql_postgresql/module.conf
new file mode 100644 (file)
index 0000000..ee9a8a9
--- /dev/null
@@ -0,0 +1,52 @@
+sql {
+       driver = "rlm_sql_postgresql"
+       dialect = "postgresql"
+
+        # Connection info:
+        #
+        server = $ENV{SQL_POSTGRESQL_TEST_SERVER}
+        port = 5432
+        login = "radius"
+        password = "radpass"
+
+        # Database table configuration for everything except Oracle
+       radius_db = "radius"
+
+       acct_table1 = "radacct"
+       acct_table2 = "radacct"
+       postauth_table = "radpostauth"
+       authcheck_table = "radcheck"
+       groupcheck_table = "radgroupcheck"
+       authreply_table = "radreply"
+       groupreply_table = "radgroupreply"
+       usergroup_table = "radusergroup"
+       read_groups = yes
+       read_profiles = yes
+
+       # Remove stale session if checkrad does not see a double login
+       delete_stale_sessions = yes
+
+       pool {
+               start = 1
+               min = 0
+               max = 1
+               spare = 3
+               uses = 2
+               lifetime = 1
+               idle_timeout = 60
+               retry_delay = 1
+       }
+
+       # Set to 'yes' to read radius clients from the database ('nas' table)
+       # Clients will ONLY be read on server startup.
+#      read_clients = yes
+
+       # Table to keep radius client info
+       client_table = "nas"
+
+       # The group attribute specific to this instance of rlm_sql
+       group_attribute = "SQL-Group"
+
+       # Read database-specific queries
+       $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
+}
diff --git a/src/tests/modules/sql_postgresql/reject.attrs b/src/tests/modules/sql_postgresql/reject.attrs
new file mode 120000 (symlink)
index 0000000..71a187f
--- /dev/null
@@ -0,0 +1 @@
+../sql/reject.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_postgresql/reject.unlang b/src/tests/modules/sql_postgresql/reject.unlang
new file mode 120000 (symlink)
index 0000000..379839f
--- /dev/null
@@ -0,0 +1 @@
+../sql/reject.unlang
\ No newline at end of file
deleted file mode 100644 (file)
index 28f905a41625a356c626b80e22fe224eacef6ba7..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-#  Input packet
-#
-User-Name = 'user@example.org'
-NAS-Port = 17826193
-NAS-IP-Address = 192.0.2.10
-Framed-IP-Address = 198.51.100.59
-NAS-Identifier = 'nas.example.org'
-Acct-Status-Type = Start
-Acct-Delay-Time = 1
-Acct-Input-Octets = 0
-Acct-Output-Octets = 0
-Acct-Session-Id = '123456789'
-Acct-Authentic = RADIUS
-Acct-Session-Time = 0
-Acct-Input-Packets = 0
-Acct-Output-Packets = 0
-Acct-Input-Gigawords = 0
-Acct-Output-Gigawords = 0
-Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
-NAS-Port-Type = Ethernet
-NAS-Port-Id = 'port 001'
-Service-Type = Framed-User
-Framed-Protocol = PPP
-Acct-Link-Count = 0
-Idle-Timeout = 0
-Session-Timeout = 604800
-Access-Loop-Encapsulation = 0x000000
-Proxy-State = 0x323531
-
-#
-#  Expected answer
-#
-#  There's not an Accounting-Failed packet type in RADIUS...
-#
-Response-Packet-Type == Access-Accept
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..24e17aee7191b910faba3cb033fbd27e07abf2c3
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_0_start.attrs
\ No newline at end of file
deleted file mode 100644 (file)
index 6a5cce1af3f628dfff37789bcc27b2a47f39c9ae..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-#  Clear out old data
-#
-update {
-       Tmp-String-0 := "%{sql_sqlite:DELETE FROM radacct WHERE 1}"
-}
-if (!&Tmp-String-0) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-sql_sqlite.accounting
-if (ok) {
-       test_pass
-}
-else {
-       test_fail
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT count(*) FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT acctsessiontime FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
-       test_fail
-}
-else {
-       test_pass
-}
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..3fe3e990e270642661009e30088d6eeff09a2e4d
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_0_start.unlang
\ No newline at end of file
deleted file mode 100644 (file)
index db8533a5f95dd80c340cd10ab8cb3be544ab2c2a..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-#  Input packet
-#
-User-Name = 'user@example.org'
-NAS-Port = 17826193
-NAS-IP-Address = 192.0.2.10
-Framed-IP-Address = 198.51.100.59
-NAS-Identifier = 'nas.example.org'
-Acct-Status-Type = Interim-Update
-Acct-Delay-Time = 1
-Acct-Input-Octets = 10
-Acct-Output-Octets = 10
-Acct-Session-Id = '123456789'
-Acct-Authentic = RADIUS
-Acct-Session-Time = 30
-Acct-Input-Packets = 10
-Acct-Output-Packets = 10
-Acct-Input-Gigawords = 1
-Acct-Output-Gigawords = 1
-Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
-NAS-Port-Type = Ethernet
-NAS-Port-Id = 'port 001'
-Service-Type = Framed-User
-Framed-Protocol = PPP
-Acct-Link-Count = 0
-Idle-Timeout = 0
-Session-Timeout = 604800
-Access-Loop-Encapsulation = 0x000000
-Proxy-State = 0x323531
-
-#
-#  Expected answer
-#
-#  There's not an Accounting-Failed packet type in RADIUS...
-#
-Response-Packet-Type == Access-Accept
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..1ab772da8a5dd663328781ab9696ce8575ef9ed2
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_1_update.attrs
\ No newline at end of file
deleted file mode 100644 (file)
index 7101d40390c5760300ae8b88758f4fe423fab5d7..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-#  PRE: acct_start
-#
-sql_sqlite.accounting
-if (ok) {
-       test_pass
-}
-else {
-       test_fail
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT count(*) FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT acctsessiontime FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..b69ff9bc5b862318c50a520a880687d08fda7987
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_1_update.unlang
\ No newline at end of file
deleted file mode 100644 (file)
index 54eb5a1b3989c1384a8d4d5c940c2e2d3b4a93c2..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-#  Input packet
-#
-User-Name = 'user@example.org'
-NAS-Port = 17826193
-NAS-IP-Address = 192.0.2.10
-Framed-IP-Address = 198.51.100.59
-NAS-Identifier = 'nas.example.org'
-Acct-Status-Type = Interim-Update
-Acct-Delay-Time = 1
-Acct-Input-Octets = 15
-Acct-Output-Octets = 15
-Acct-Session-Id = '123456789'
-Acct-Authentic = RADIUS
-Acct-Session-Time = 60
-Acct-Input-Packets = 15
-Acct-Output-Packets = 15
-Acct-Input-Gigawords = 1
-Acct-Output-Gigawords = 1
-Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
-NAS-Port-Type = Ethernet
-NAS-Port-Id = 'port 001'
-Service-Type = Framed-User
-Framed-Protocol = PPP
-Acct-Link-Count = 0
-Idle-Timeout = 0
-Session-Timeout = 604800
-Access-Loop-Encapsulation = 0x000000
-Proxy-State = 0x323531
-
-#
-#  Expected answer
-#
-#  There's not an Accounting-Failed packet type in RADIUS...
-#
-Response-Packet-Type == Access-Accept
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..ea73931dd21defdf839de3df457f3d22a242b237
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_2_stop.attrs
\ No newline at end of file
deleted file mode 100644 (file)
index a7ff59601cbecc44738504ff8ea35ad829394945..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-#  PRE: acct_update
-#
-sql_sqlite.accounting
-if (ok) {
-       test_pass
-}
-else {
-       test_fail
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT count(*) FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT acctsessiontime FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 60)) {
-       test_fail
-}
-else {
-       test_pass
-}
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..ea0be5693c6264bd409dcb74c96e583bb79582fa
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_2_stop.unlang
\ No newline at end of file
deleted file mode 100644 (file)
index 28f905a41625a356c626b80e22fe224eacef6ba7..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-#  Input packet
-#
-User-Name = 'user@example.org'
-NAS-Port = 17826193
-NAS-IP-Address = 192.0.2.10
-Framed-IP-Address = 198.51.100.59
-NAS-Identifier = 'nas.example.org'
-Acct-Status-Type = Start
-Acct-Delay-Time = 1
-Acct-Input-Octets = 0
-Acct-Output-Octets = 0
-Acct-Session-Id = '123456789'
-Acct-Authentic = RADIUS
-Acct-Session-Time = 0
-Acct-Input-Packets = 0
-Acct-Output-Packets = 0
-Acct-Input-Gigawords = 0
-Acct-Output-Gigawords = 0
-Event-Timestamp = 'Feb  1 2015 08:28:58 WIB'
-NAS-Port-Type = Ethernet
-NAS-Port-Id = 'port 001'
-Service-Type = Framed-User
-Framed-Protocol = PPP
-Acct-Link-Count = 0
-Idle-Timeout = 0
-Session-Timeout = 604800
-Access-Loop-Encapsulation = 0x000000
-Proxy-State = 0x323531
-
-#
-#  Expected answer
-#
-#  There's not an Accounting-Failed packet type in RADIUS...
-#
-Response-Packet-Type == Access-Accept
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..117a505aab87aa1997884a83fc82a954fb6d236f
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_start_conflict.attrs
\ No newline at end of file
deleted file mode 100644 (file)
index 2d2cf2e571df71502bde22c535b9e4d764bf5852..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,76 +0,0 @@
-#
-#  Check that conflicting unique IDs triggers failover to alternative query
-#
-
-#
-#  Clear out old data
-#
-update {
-       Tmp-String-0 := "%{sql_sqlite:DELETE FROM radacct WHERE 1}"
-}
-if (!&Tmp-String-0) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-#
-#  Insert the Accounting-Request start
-#
-sql_sqlite.accounting
-if (ok) {
-       test_pass
-}
-else {
-       test_fail
-}
-
-#
-#  Check the database has at least one row
-#
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT count(*) FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-#
-#  Check acctsessiontime matches the value in the request
-#
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT acctsessiontime FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 0)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-#
-#  Change acctsessiontime and verify it's updated
-#
-update request {
-       Connect-Info = 'updated'
-}
-sql_sqlite.accounting
-if (ok) {
-       test_pass
-}
-else {
-       test_pass
-}
-update {
-       Tmp-String-0 := "%{sql_sqlite:SELECT connectinfo_start FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-String-0 != 'updated')) {
-       test_fail
-}
-else {
-       test_pass
-}
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..da357987801874fb0b98cacbc85fa8737ad856f7
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_start_conflict.unlang
\ No newline at end of file
deleted file mode 100644 (file)
index db8533a5f95dd80c340cd10ab8cb3be544ab2c2a..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-#  Input packet
-#
-User-Name = 'user@example.org'
-NAS-Port = 17826193
-NAS-IP-Address = 192.0.2.10
-Framed-IP-Address = 198.51.100.59
-NAS-Identifier = 'nas.example.org'
-Acct-Status-Type = Interim-Update
-Acct-Delay-Time = 1
-Acct-Input-Octets = 10
-Acct-Output-Octets = 10
-Acct-Session-Id = '123456789'
-Acct-Authentic = RADIUS
-Acct-Session-Time = 30
-Acct-Input-Packets = 10
-Acct-Output-Packets = 10
-Acct-Input-Gigawords = 1
-Acct-Output-Gigawords = 1
-Event-Timestamp = 'Feb  1 2015 08:28:28 WIB'
-NAS-Port-Type = Ethernet
-NAS-Port-Id = 'port 001'
-Service-Type = Framed-User
-Framed-Protocol = PPP
-Acct-Link-Count = 0
-Idle-Timeout = 0
-Session-Timeout = 604800
-Access-Loop-Encapsulation = 0x000000
-Proxy-State = 0x323531
-
-#
-#  Expected answer
-#
-#  There's not an Accounting-Failed packet type in RADIUS...
-#
-Response-Packet-Type == Access-Accept
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..328867f219a2f975ebab16f956ebc9ddd79eada9
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_update_no_start.attrs
\ No newline at end of file
deleted file mode 100644 (file)
index 90d9597e21d0c69e1e6882df17f26324d5439c58..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-#  Clear out old data
-#
-update {
-       Tmp-String-0 := "%{sql_sqlite:DELETE FROM radacct WHERE 1}"
-}
-if (!&Tmp-String-0) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-sql_sqlite.accounting
-if (ok) {
-       test_pass
-}
-else {
-       test_fail
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT count(*) FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 1)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
-update {
-       Tmp-Integer-0 := "%{sql_sqlite:SELECT acctsessiontime FROM radacct WHERE 1}"
-}
-if (!&Tmp-Integer-0 || (&Tmp-Integer-0 != 30)) {
-       test_fail
-}
-else {
-       test_pass
-}
-
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..6837977f86329ee89fe09db30d66d22451080512
--- /dev/null
@@ -0,0 +1 @@
+../sql/acct_update_no_start.unlang
\ No newline at end of file
index 95e9b80..a7907f1 100644 (file)
@@ -1,18 +1,3 @@
 #
 #  Test the sqlite module
 #
-
-#  MODULE.test is the main target for this module.
-sql_sqlite.test:
-       @echo OK: sql_sqlite.test
-
-SQLITE_TESTDIR := $(BUILD_DIR)/tests/modules/sql_sqlite
-
-$(SQLITE_TESTDIR)/acct_update_no_start: $(SQLITE_TESTDIR)/acct_start_conflict
-
-$(SQLITE_TESTDIR)/acct_start_conflict: $(SQLITE_TESTDIR)/acct_2_stop
-
-$(SQLITE_TESTDIR)/acct_2_stop: $(SQLITE_TESTDIR)/acct_1_update
-
-$(SQLITE_TESTDIR)/acct_1_update: $(SQLITE_TESTDIR)/acct_0_start
-
diff --git a/src/tests/modules/sql_sqlite/auth.attrs b/src/tests/modules/sql_sqlite/auth.attrs
new file mode 120000 (symlink)
index 0000000..6b30b6b
--- /dev/null
@@ -0,0 +1 @@
+../sql/auth.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_sqlite/auth.unlang b/src/tests/modules/sql_sqlite/auth.unlang
new file mode 120000 (symlink)
index 0000000..3ccd80e
--- /dev/null
@@ -0,0 +1 @@
+../sql/auth.unlang
\ No newline at end of file
index 11fa277..1d8ac74 100644 (file)
@@ -1,9 +1,9 @@
-sql sql_sqlite {
+sql {
        driver = "rlm_sql_sqlite"
        dialect = "sqlite"
        sqlite {
                # Path to the sqlite database
-               filename = "$ENV{MODULE_TEST_DIR}/rlm_sql_sqlite.db"
+               filename = "$ENV{MODULE_TEST_DIR}/sql_sqlite/rlm_sql_sqlite.db"
 
                # If the file above does not exist and bootstrap is set
                # a new database file will be created, and the SQL statements
@@ -44,6 +44,9 @@ sql sql_sqlite {
        # Table to keep radius client info
        client_table = "nas"
 
+       # The group attribute specific to this instance of rlm_sql
+       group_attribute = "SQL-Group"
+
        # Read database-specific queries
        $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
 }
diff --git a/src/tests/modules/sql_sqlite/reject.attrs b/src/tests/modules/sql_sqlite/reject.attrs
new file mode 120000 (symlink)
index 0000000..71a187f
--- /dev/null
@@ -0,0 +1 @@
+../sql/reject.attrs
\ No newline at end of file
diff --git a/src/tests/modules/sql_sqlite/reject.unlang b/src/tests/modules/sql_sqlite/reject.unlang
new file mode 120000 (symlink)
index 0000000..379839f
--- /dev/null
@@ -0,0 +1 @@
+../sql/reject.unlang
\ No newline at end of file
diff --git a/src/tests/modules/test.mk b/src/tests/modules/test.mk
new file mode 100644 (file)
index 0000000..b217ff7
--- /dev/null
@@ -0,0 +1,165 @@
+#
+#  Add the module tests to the overall dependencies
+#
+
+TESTS.MODULES_FILES :=
+
+# If module requires test server, make sure TEST_SERVER of <MODULE>_TEST_SERVER variables are defined
+# If TEST_SERVER is defined, define <MODULE>_TEST_SERVER for all modules that have CHECK_MODULE_TEST_CAN_BE_RUN
+define CHECK_MODULE_TEST_CAN_BE_RUN
+  ifndef ${1}_require_test_server
+    tests.modules: ${1}.test
+  else
+    ifdef TEST_SERVER
+      tests.modules: ${1}.test
+      export $(shell echo ${1} | tr a-z A-Z)_TEST_SERVER := $(TEST_SERVER)
+    endif
+    ifdef $(shell echo ${1} | tr a-z A-Z)_TEST_SERVER
+      tests.modules: ${1}.test
+    endif
+  endif
+endef
+$(foreach x,$(TEST_BUILT) $(TEST_SUBBUILT),$(eval $(call CHECK_MODULE_TEST_CAN_BE_RUN,$x)))
+
+######################################################################
+#
+#  And now more makefile magic to automatically run the tests
+#  for each module.
+#
+
+define DEFAULT_ATTRS
+ifeq "$(wildcard ${1}.attrs)"
+${1}.attrs
+else
+src/tests/modules/default-input.attrs
+endif
+endef
+
+#
+#  Files in the output dir depend on the unit tests
+#
+#      src/tests/$(MODULE_DIR)/FOO.unlang      unlang for the test
+#      src/tests/$(MODULE_DIR)/FOO.attrs       input RADIUS and output filter
+#      build/tests/$(MODULE_DIR)/FOO.out       updated if the test succeeds
+#      build/tests/$(MODULE_DIR)/FOO.log       debug output for the test
+#
+#  If the test fails, then look for ERROR in the input.  No error
+#  means it's unexpected, so we die.
+#
+#  Otherwise, check the log file for a parse error which matches the
+#  ERROR line in the input.
+#
+$(BUILD_DIR)/tests/modules/%: src/tests/modules/%.unlang $(BUILD_DIR)/tests/modules/%.attrs $(TESTBINDIR)/unittest | build.raddb
+       @mkdir -p $(dir $@)
+       @echo MODULE-TEST $(lastword $(subst /, ,$(dir $@))) $(basename $(notdir $@))
+       @if ! MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xxx > $@.log 2>&1; then \
+               if ! grep ERROR $< 2>&1 > /dev/null; then \
+                       cat $@.log; \
+                       echo "# $@.log"; \
+                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
+                       exit 1; \
+               fi; \
+               FOUND=$$(grep ^$< $@.log | head -1 | sed 's/:.*//;s/.*\[//;s/\].*//'); \
+               EXPECTED=$$(grep -n ERROR $< | sed 's/:.*//'); \
+               if [ "$$EXPECTED" != "$$FOUND" ]; then \
+                       cat $@.log; \
+                       echo "# $@.log"; \
+                       echo MODULE_TEST_DIR=$(dir $<) MODULE_TEST_UNLANG=$< $(TESTBIN)/unittest -D share -d src/tests/modules/ -i $@.attrs -f $@.attrs -xx; \
+                       exit 1; \
+               fi \
+       fi
+       @touch $@
+
+#
+#  Sometimes we have a default input.  So use that.  Otherwise, use
+#  the input specific to the test.
+#
+MODULE_UNLANG          := $(wildcard src/tests/modules/*/*.unlang src/tests/modules/*/*/*.unlang)
+MODULE_ATTRS_REQUIRES  := $(patsubst %.unlang,%.attrs,$(MODULE_UNLANG))
+MODULE_ATTRS_EXISTS    := $(wildcard src/tests/modules/*/*.attrs src/tests/modules/*/*/*.attrs)
+MODULE_ATTRS_NEEDS     := $(filter-out $(MODULE_ATTRS_EXISTS),$(MODULE_ATTRS_REQUIRES))
+
+MODULE_CONF_REQUIRES   := $(patsubst %.unlang,%.conf,$(MODULE_UNLANG))
+MODULE_CONF_EXISTS     := $(wildcard src/tests/modules/*/*.conf src/tests/modules/*/*/*.attrs)
+MODULE_CONF_NEEDS      := $(filter-out $(MODULE_CONF_EXISTS),$(MODULE_CONF_REQUIRES))
+
+#
+#  The complete list of tests which are to be run
+#
+MODULE_TESTS           := $(patsubst src/tests/modules/%/all.mk,%,$(wildcard src/tests/modules/*/all.mk))
+
+
+#
+#  Target-specific rules
+#
+define MODULE_COPY_FILE
+$(BUILD_DIR)/${1}: src/${1}
+       @mkdir -p $$(@D)
+       @cp $$< $$@
+
+endef
+
+#
+#  Default rules
+#
+define MODULE_COPY_ATTR
+$(BUILD_DIR)/${1}: src/tests/modules/default-input.attrs
+       @mkdir -p $$(@D)
+       @cp $$< $$@
+endef
+
+#
+#  FIXME: get this working
+#
+define MODULE_COPY_CONF
+$(BUILD_DIR)/${1}: src/tests/modules/${2}/module.conf
+       @mkdir -p $$(@D)
+       @cp $$< $$@
+endef
+
+define MODULE_FILE_TARGET
+$(BUILD_DIR)/${1}: src/${1}.unlang $(BUILD_DIR)/${1}.attrs
+
+endef
+
+define MODULE_TEST_TARGET
+${1}.test: $(patsubst %.unlang,%,$(subst src,$(BUILD_DIR),$(filter src/tests/modules/${1}/%,$(MODULE_UNLANG))))
+
+TESTS.MODULES_FILES += $(patsubst %.unlang,%,$(subst src,$(BUILD_DIR),$(filter src/tests/modules/${1}/%,$(MODULE_UNLANG))))
+endef
+
+#
+#  Create the rules from the list of input files
+#
+$(foreach x,$(MODULE_ATTRS_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
+$(foreach x,$(MODULE_CONF_EXISTS),$(eval $(call MODULE_COPY_FILE,$(subst src/,,$x))))
+
+$(foreach x,$(MODULE_ATTRS_NEEDS),$(eval $(call MODULE_COPY_ATTR,$(subst src/,,$x))))
+# FIXME: copy src/tests/modules/*/module.conf to the right place, too
+
+$(foreach x,$(MODULE_UNLANG),$(eval $(call MODULE_FILE_TARGET,$(patsubst %.unlang,%,$(subst src/,,$x)))))
+$(foreach x,$(MODULE_TESTS),$(eval $(call MODULE_TEST_TARGET,$x)))
+
+$(TESTS.MODULES_FILES): $(TESTS.AUTH_FILES)
+
+.PHONY: clean.modules.test
+clean.modules.test:
+       @rm -rf $(BUILD_DIR)/tests/modules/
+
+#
+#  For each file, look for precursor test.
+#  Ensure that each test depends on its precursors.
+#
+-include $(BUILD_DIR)/tests/modules/depends.mk
+
+$(BUILD_DIR)/tests/modules/depends.mk: $(MODULE_UNLANG) | $(BUILD_DIR)/tests/modules
+       @rm -f $@
+       @for x in $^; do \
+               y=`grep PRE $$x | awk '{ print $$3 }'`; \
+               if [ "$$y" != "" ]; then \
+                       z=`echo $$x | sed 's,src/,$(BUILD_DIR)/', | sed 's/.unlang//'`; \
+                       d=$$(basename $$(dirname $$x)); \
+                       echo "$$z: $(BUILD_DIR)/tests/modules/$$d/$$y" >> $@; \
+                       echo "" >> $@; \
+               fi \
+       done
diff --git a/src/tests/peap-client-mschapv2.conf b/src/tests/peap-client-mschapv2.conf
new file mode 100644 (file)
index 0000000..1c60933
--- /dev/null
@@ -0,0 +1,18 @@
+#
+#   ./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"
+       phase1="peapver=0"
+
+       ca_cert="../../raddb/certs/ca.pem"
+       client_cert="../../raddb/certs/client.crt"
+       private_key="../../raddb/certs/client.key"
+       private_key_passwd="whatever"
+}
index 305e7fd..e5e1769 100644 (file)
@@ -213,7 +213,7 @@ again:
 
         *
         */
-       rbtree_walk(t, RBTREE_DELETE_ORDER, filter_cb, &thresh);
+       (void) 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; }
diff --git a/src/tests/salt-test-server/.gitignore b/src/tests/salt-test-server/.gitignore
new file mode 100644 (file)
index 0000000..e3aa327
--- /dev/null
@@ -0,0 +1,8 @@
+# Local files
+*.swp
+.DS_Store
+*.md5
+
+# Salt runtime directories
+tmp
+cache
diff --git a/src/tests/salt-test-server/README b/src/tests/salt-test-server/README
new file mode 100644 (file)
index 0000000..8265f46
--- /dev/null
@@ -0,0 +1,3 @@
+Salt script to build the test VM required for running the ldap, mysql & postgres tests.
+
+See http://docs.saltstack.com/en/latest/index.html
diff --git a/src/tests/salt-test-server/build.sh b/src/tests/salt-test-server/build.sh
new file mode 100755 (executable)
index 0000000..ad1873b
--- /dev/null
@@ -0,0 +1 @@
+salt-ssh --config-dir=salt_config -l quiet "test-server" state.highstate
diff --git a/src/tests/salt-test-server/salt/iptable.sls b/src/tests/salt-test-server/salt/iptable.sls
new file mode 100644 (file)
index 0000000..7aefdd1
--- /dev/null
@@ -0,0 +1,13 @@
+{% if grains['os'] == 'CentOS' %}
+update_firewall:
+    file.managed:
+        - name: /etc/sysconfig/iptables
+        - source: salt://iptables
+
+reload_iptables:
+    cmd.wait:
+        - cwd: /
+        - name: service iptables reload
+        - watch:
+            - file: /etc/sysconfig/iptables
+{% endif %}
diff --git a/src/tests/salt-test-server/salt/iptables b/src/tests/salt-test-server/salt/iptables
new file mode 100644 (file)
index 0000000..2e2d4a2
--- /dev/null
@@ -0,0 +1,15 @@
+# Generated by iptables-save v1.4.7 on Thu Feb 19 13:41:09 2015
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 5432 -j ACCEPT
+-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
+-A INPUT -p icmp -j ACCEPT
+-A INPUT -i lo -j ACCEPT
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+-A INPUT -j REJECT --reject-with icmp-host-prohibited
+-A FORWARD -j REJECT --reject-with icmp-host-prohibited
+COMMIT
+# Completed on Thu Feb 19 13:41:09 2015
diff --git a/src/tests/salt-test-server/salt/ldap.sls b/src/tests/salt-test-server/salt/ldap.sls
new file mode 100644 (file)
index 0000000..006abf8
--- /dev/null
@@ -0,0 +1,41 @@
+{% if grains['os'] == 'Ubuntu' %}
+
+# In Ubuntu 14.10, openldap comes with a broken AppArmor profile (can't connect through socket)
+# Disable AppArmor alltogether
+/etc/init.d/apparmor teardown:
+   cmd.run
+
+update-rc.d -f apparmor remove:
+   cmd.run
+
+{% endif %}
+
+slapd:
+    pkg.installed
+
+ldap-utils:
+    pkg.installed
+
+# Copy ldif file for base structure
+/root/base.ldif:
+    file.managed:
+        - source: salt://ldap/base.ldif
+
+# Copy ldif file for FreeRADIUS schema
+/root/schema_freeradius.ldif:
+    file.managed:
+        - source: salt://ldap/schema_freeradius.ldif
+
+# Add FreeRADIUS schema
+add_fr_schema:
+    cmd.run:
+        - name: "ldapadd -Y EXTERNAL -H ldapi:/// -f /root/schema_freeradius.ldif"
+        - cwd: /root/
+        - unless: "/usr/bin/ldapsearch -Y EXTERNAL -H ldapi:/// -b cn={4}radius,cn=schema,cn=config -s base"
+
+# Create base structure in LDAP
+build_base_structure:
+    cmd.run:
+        - name: "/usr/bin/ldapadd -Y EXTERNAL -H ldapi:/// -f /root/base.ldif"
+        - cwd: /root/
+        - unless: "/usr/bin/ldapsearch -Y EXTERNAL -H ldapi:/// -b dc=example,dc=com -s base"
diff --git a/src/tests/salt-test-server/salt/ldap/base.ldif b/src/tests/salt-test-server/salt/ldap/base.ldif
new file mode 100644 (file)
index 0000000..1cf5d97
--- /dev/null
@@ -0,0 +1,84 @@
+# Database settings
+dn: olcDatabase=hdb,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcHdbConfig
+olcDatabase: {1}hdb
+olcSuffix: dc=example,dc=com
+olcDbDirectory: /var/lib/ldap
+olcRootDN: cn=admin,dc=example,dc=com
+olcRootPW: {SSHA}SgCZuAcGQA5HlgKi+g5xwVyI2NhXRFYh
+olcDbConfig: set_cachesize 0 2097152 0
+olcDbConfig: set_lk_max_objects 1500
+olcDbConfig: set_lk_max_locks 1500
+olcDbConfig: set_lk_max_lockers 1500
+olcDbIndex: objectClass eq
+olcLastMod: TRUE
+olcDbCheckpoint: 512 30
+olcAccess: to * by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
+olcAccess: to attrs=userPassword by dn="cn=admin,dc=example,dc=com" write by anonymous auth by self write by * none
+olcAccess: to attrs=shadowLastChange by self write by * read
+olcAccess: to dn.base="" by * read
+olcAccess: to * by dn="cn=admin,dc=example,dc=com" write by * read
+
+# Create top-level object in domain
+dn: dc=example,dc=com
+objectClass: top
+objectClass: dcObject
+objectclass: organization
+o: Example Organization
+dc: Example
+description: LDAP Example
+
+dn: ou=people,dc=example,dc=com
+objectClass: organizationalUnit
+ou: people
+
+dn: ou=groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: groups
+
+# foo, groups, example.com
+dn: cn=foo,ou=groups,dc=example,dc=com
+cn: foo
+objectClass: groupOfNames
+objectClass: top
+member: uid=john,ou=people,dc=example,dc=com
+
+dn: ou=profiles,dc=example,dc=com
+objectClass: organizationalUnit
+ou: profiles
+
+dn: cn=radprofile,ou=profiles,dc=example,dc=com
+objectClass: radiusObjectProfile
+objectClass: radiusprofile
+cn: radprofile
+radiusFramedIPNetmask: 255.255.255.0
+
+dn: cn=profile1,ou=profiles,dc=example,dc=com
+objectClass: radiusObjectProfile
+objectClass: radiusprofile
+cn: profile1
+radiusReplyAttribute: Framed-IP-Netmask := 255.255.0.0
+radiusReplyAttribute: Acct-Interim-Interval := 1800
+radiusRequestAttribute: Service-Type := Framed-User
+radiusControlAttribute: Framed-IP-Address == 1.2.3.4
+radiusControlAttribute: Reply-Message == "Hello world"
+
+dn: uid=john,ou=people,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: radiusprofile
+uid: john
+sn: Doe
+givenName: John
+cn: John Doe
+displayName: John Doe
+userPassword: {cleartext}password
+uidNumber: 100
+gidNumber: 100
+homeDirectory: /home/john
+radiusIdleTimeout: 3600
+radiusAttribute: reply:Session-Timeout := 7200
+radiusAttribute: control:NAS-IP-Address := 1.2.3.4
+radiusProfileDN: cn=profile1,ou=profiles,dc=example,dc=com
diff --git a/src/tests/salt-test-server/salt/ldap/schema_freeradius.ldif b/src/tests/salt-test-server/salt/ldap/schema_freeradius.ldif
new file mode 100644 (file)
index 0000000..44d2cb9
--- /dev/null
@@ -0,0 +1,76 @@
+dn: cn=radius,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: radius
+olcAttributeTypes: {0}( 1.3.6.1.4.1.11344.4.3.1.1 NAME 'radiusArapFeatures' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {1}( 1.3.6.1.4.1.11344.4.3.1.2 NAME 'radiusArapSecurity' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {2}( 1.3.6.1.4.1.11344.4.3.1.3 NAME 'radiusArapZoneAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {3}( 1.3.6.1.4.1.11344.4.3.1.44 NAME 'radiusAuthType' DESC 'controlItem: Auth-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {4}( 1.3.6.1.4.1.11344.4.3.1.4 NAME 'radiusCallbackId' DESC 'replyItem: Callback-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {5}( 1.3.6.1.4.1.11344.4.3.1.5 NAME 'radiusCallbackNumber' DESC 'replyItem: Callback-Number' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {6}( 1.3.6.1.4.1.11344.4.3.1.6 NAME 'radiusCalledStationId' DESC 'controlItem: Called-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {7}( 1.3.6.1.4.1.11344.4.3.1.7 NAME 'radiusCallingStationId' DESC 'controlItem: Calling-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {8}( 1.3.6.1.4.1.11344.4.3.1.8 NAME 'radiusClass' DESC 'replyItem: Class' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {9}( 1.3.6.1.4.1.11344.4.3.1.45 NAME 'radiusClientIPAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {10}( 1.3.6.1.4.1.11344.4.3.1.9 NAME 'radiusFilterId' DESC 'replyItem: Filter-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {11}( 1.3.6.1.4.1.11344.4.3.1.10 NAME 'radiusFramedAppleTalkLink' DESC 'replyItem: Framed-AppleTalk-Link' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {12}( 1.3.6.1.4.1.11344.4.3.1.11 NAME 'radiusFramedAppleTalkNetwork' DESC 'replyItem: Framed-AppleTalk-Network' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {13}( 1.3.6.1.4.1.11344.4.3.1.12 NAME 'radiusFramedAppleTalkZone' DESC 'replyItem: Framed-AppleTalk-Zone' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {14}( 1.3.6.1.4.1.11344.4.3.1.13 NAME 'radiusFramedCompression' DESC 'replyItem: Framed-Compression' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {15}( 1.3.6.1.4.1.11344.4.3.1.14 NAME 'radiusFramedIPAddress' DESC 'replyItem: Framed-IP-Address' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {16}( 1.3.6.1.4.1.11344.4.3.1.15 NAME 'radiusFramedIPNetmask' DESC 'replyItem: Framed-IP-Netmask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {17}( 1.3.6.1.4.1.11344.4.3.1.16 NAME 'radiusFramedIPXNetwork' DESC 'replyItem: Framed-IPX-Network' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {18}( 1.3.6.1.4.1.11344.4.3.1.17 NAME 'radiusFramedMTU' DESC' replyItem: Framed-MTU' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {19}( 1.3.6.1.4.1.11344.4.3.1.18 NAME 'radiusFramedProtocol'DESC 'replyItem: Framed-Protocol' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {20}( 1.3.6.1.4.1.11344.4.3.1.19 NAME 'radiusFramedRoute' DESC 'replyItem: Framed-Route' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {21}( 1.3.6.1.4.1.11344.4.3.1.20 NAME 'radiusFramedRouting' DESC 'replyItem: Framed-Routing' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {22}( 1.3.6.1.4.1.11344.4.3.1.46 NAME 'radiusGroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {23}( 1.3.6.1.4.1.11344.4.3.1.47 NAME 'radiusHint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {24}( 1.3.6.1.4.1.11344.4.3.1.48 NAME 'radiusHuntgroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {25}( 1.3.6.1.4.1.11344.4.3.1.21 NAME 'radiusIdleTimeout' DESC 'replyItem: Idle-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {26}( 1.3.6.1.4.1.11344.4.3.1.22 NAME 'radiusLoginIPHost' DESC 'replyItem: Login-IP-Host' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {27}( 1.3.6.1.4.1.11344.4.3.1.23 NAME 'radiusLoginLATGroup' DESC 'replyItem: Login-LAT-Group' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {28}( 1.3.6.1.4.1.11344.4.3.1.24 NAME 'radiusLoginLATNode' DESC 'replyItem: Login-LAT-Node' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {29}( 1.3.6.1.4.1.11344.4.3.1.25 NAME 'radiusLoginLATPort' DESC 'replyItem: Login-LAT-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {30}( 1.3.6.1.4.1.11344.4.3.1.26 NAME 'radiusLoginLATService' DESC 'replyItem: Login-LAT-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {31}( 1.3.6.1.4.1.11344.4.3.1.27 NAME 'radiusLoginService' DESC 'replyItem: Login-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {32}( 1.3.6.1.4.1.11344.4.3.1.28 NAME 'radiusLoginTCPPort' DESC 'replyItem: Login-TCP-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {33}( 1.3.6.1.4.1.11344.4.3.1.29 NAME 'radiusPasswordRetry' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {34}( 1.3.6.1.4.1.11344.4.3.1.30 NAME 'radiusPortLimit' DESC 'replyItem: Port-Limit' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {35}( 1.3.6.1.4.1.11344.4.3.1.49 NAME 'radiusProfileDN' DESC '' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: {36}( 1.3.6.1.4.1.11344.4.3.1.31 NAME 'radiusPrompt' DESC ''EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {37}( 1.3.6.1.4.1.11344.4.3.1.50 NAME 'radiusProxyToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {38}( 1.3.6.1.4.1.11344.4.3.1.51 NAME 'radiusReplicateToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {39}( 1.3.6.1.4.1.11344.4.3.1.52 NAME 'radiusRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE)
+olcAttributeTypes: {40}( 1.3.6.1.4.1.11344.4.3.1.32 NAME 'radiusServiceType' DESC 'replyItem: Service-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {41}( 1.3.6.1.4.1.11344.4.3.1.33 NAME 'radiusSessionTimeout'DESC 'replyItem: Session-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {42}( 1.3.6.1.4.1.11344.4.3.1.34 NAME 'radiusTerminationAction' DESC 'replyItem: Termination-Action' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {43}( 1.3.6.1.4.1.11344.4.3.1.35 NAME 'radiusTunnelAssignmentId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26)
+olcAttributeTypes: {44}( 1.3.6.1.4.1.11344.4.3.1.36 NAME 'radiusTunnelMediumType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {45}( 1.3.6.1.4.1.11344.4.3.1.37 NAME 'radiusTunnelPassword' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {46}( 1.3.6.1.4.1.11344.4.3.1.38 NAME 'radiusTunnelPreference' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {47}( 1.3.6.1.4.1.11344.4.3.1.39 NAME 'radiusTunnelPrivateGroupId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {48}( 1.3.6.1.4.1.11344.4.3.1.40 NAME 'radiusTunnelServerEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {49}( 1.3.6.1.4.1.11344.4.3.1.41 NAME 'radiusTunnelType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {50}( 1.3.6.1.4.1.11344.4.3.1.42 NAME 'radiusVSA' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {51}( 1.3.6.1.4.1.11344.4.3.1.43 NAME 'radiusTunnelClientEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {52}( 1.3.6.1.4.1.11344.4.3.1.53 NAME 'radiusSimultaneousUse' DESC 'controlItem: Simultaneous-Use' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: {53}( 1.3.6.1.4.1.11344.4.3.1.54 NAME 'radiusLoginTime' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {54}( 1.3.6.1.4.1.11344.4.3.1.55 NAME 'radiusUserCategory' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {55}( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+olcAttributeTypes: {56}( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {57}( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC 'controlItem: Expiration' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {58}( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {59}( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusNASIpAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {60}( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusReplyMessage' DESC 'replyItem: Reply-Message' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {61}( 1.3.6.1.4.1.11344.4.3.1.63 NAME 'radiusControlAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {62}( 1.3.6.1.4.1.11344.4.3.1.64 NAME 'radiusReplyAttribute' DESC 'replyItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {63}( 1.3.6.1.4.1.11344.4.3.1.65 NAME 'radiusRequestAttribute' DESC 'requestItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcObjectClasses: {0}( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SUP top AUXILIARY MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $
+ radiusCallbackId $ radiusCallbackNumber $radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $
+ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $radiusFramedProtocol $ radiusAttribute $
+ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $
+ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $
+ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $
+ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $
+ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute ) )
+olcObjectClasses: {1}( 1.3.6.1.4.1.11344.4.3.2.2 NAME 'radiusObjectProfile' DESC 'A Container Objectclass to be used for creating radius profile object' SUP top STRUCTURAL MUST cn MAY ( uid $ userPassword $ description ) )
diff --git a/src/tests/salt-test-server/salt/mysql.sls b/src/tests/salt-test-server/salt/mysql.sls
new file mode 100644 (file)
index 0000000..df1ea00
--- /dev/null
@@ -0,0 +1,74 @@
+mysql-server:
+    pkg.installed
+
+# On Ubuntu, the default MySQL install only listens on localhost
+/etc/mysql/my.cnf:
+{% if grains['os'] == 'Ubuntu' %}
+    file.sed:
+        - before: 127.0.0.1
+        - after: 0.0.0.0
+        - limit: ^bind-address\s+=
+        - require:
+            - pkg: mysql-server
+{% else %}
+    file.exists
+{% endif %}
+
+mysql_daemon:
+    service:
+{% if grains['os'] == 'CentOS' %}
+        - name: mysqld
+{% elif grains['os'] == 'Ubuntu' or grains['os'] == 'Debian' %}
+        - name: mysql
+{% endif %}
+        - running
+        - enable: True
+        - watch:
+            - file: /etc/mysql/my.cnf
+        - require:
+            - pkg: mysql-server
+
+## FW rules don't work well with CentOS < 7
+# Insert is run each time
+#
+#    iptables.insert:
+#        - position: 1
+#        - table: filter
+#        - chain: INPUT
+#        - j: ACCEPT        # Use 'j' instead of 'jump' because iptables-save outputs 'j' flag.
+#        - match: state
+#        - connstate: NEW
+#        - dport: 3306
+#        - proto: tcp
+#        - save: True
+
+# Copy DB schema file
+/salt/mysql/schema.sql:
+    file.managed:
+        - source: salt://mysql/schema.sql
+        - makedirs: true
+
+# Copy DB setup script
+/salt/mysql/setup.sql:
+    file.managed:
+        - source: salt://mysql/setup.sql
+        - makedirs: true
+
+# Create DB
+echo "CREATE DATABASE radius" | mysql:
+    cmd.run:
+        - creates: /var/lib/mysql/radius/db.opt
+
+# Create FreeRADIUS schema
+mysql radius < /salt/mysql/schema.sql:
+    cmd.run:
+        - unless: "echo 'desc radacct' | mysql radius"
+        - require:
+            - file: /salt/mysql/schema.sql
+
+# Setup DB access
+mysql radius < /salt/mysql/setup.sql:
+    cmd.run:
+        - unless: "echo \"show grants for 'radius';\" | mysql"
+        - require:
+            - file: /salt/mysql/setup.sql
diff --git a/src/tests/salt-test-server/salt/mysql/schema.sql b/src/tests/salt-test-server/salt/mysql/schema.sql
new file mode 100644 (file)
index 0000000..7761a62
--- /dev/null
@@ -0,0 +1,150 @@
+###########################################################################
+# $Id$                 #
+#                                                                         #
+#  schema.sql                       rlm_sql - FreeRADIUS SQL Module       #
+#                                                                         #
+#     Database schema for MySQL rlm_sql module                            #
+#                                                                         #
+#     To load:                                                            #
+#         mysql -uroot -prootpass radius < schema.sql                     #
+#                                                                         #
+#                                   Mike Machado <mike@innercite.com>     #
+###########################################################################
+#
+# Table structure for table 'radacct'
+#
+
+CREATE TABLE radacct (
+  radacctid bigint(21) NOT NULL auto_increment,
+  acctsessionid varchar(64) NOT NULL default '',
+  acctuniqueid varchar(32) NOT NULL default '',
+  username varchar(64) NOT NULL default '',
+  groupname varchar(64) NOT NULL default '',
+  realm varchar(64) default '',
+  nasipaddress varchar(15) NOT NULL default '',
+  nasportid varchar(50) default NULL,
+  nasporttype varchar(32) default NULL,
+  acctstarttime datetime NULL default NULL,
+  acctupdatetime datetime NULL default NULL,
+  acctstoptime datetime NULL default NULL,
+  acctinterval 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,
+  acctinputoctets bigint(20) default NULL,
+  acctoutputoctets bigint(20) default NULL,
+  calledstationid varchar(50) NOT NULL default '',
+  callingstationid varchar(50) NOT NULL default '',
+  acctterminatecause varchar(32) NOT NULL default '',
+  servicetype varchar(32) default NULL,
+  framedprotocol varchar(32) default NULL,
+  framedipaddress varchar(15) NOT NULL default '',
+  PRIMARY KEY (radacctid),
+  UNIQUE KEY acctuniqueid (acctuniqueid),
+  KEY username (username),
+  KEY framedipaddress (framedipaddress),
+  KEY acctsessionid (acctsessionid),
+  KEY acctsessiontime (acctsessiontime),
+  KEY acctstarttime (acctstarttime),
+  KEY acctinterval (acctinterval),
+  KEY acctstoptime (acctstoptime),
+  KEY nasipaddress (nasipaddress)
+) ENGINE = INNODB;
+
+#
+# Table structure for table 'radcheck'
+#
+
+CREATE TABLE radcheck (
+  id int(11) unsigned NOT NULL auto_increment,
+  username varchar(64) NOT NULL default '',
+  attribute varchar(64)  NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '==',
+  value varchar(253) NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY username (username(32))
+);
+
+#
+# Table structure for table 'radgroupcheck'
+#
+
+CREATE TABLE radgroupcheck (
+  id int(11) unsigned NOT NULL auto_increment,
+  groupname varchar(64) NOT NULL default '',
+  attribute varchar(64)  NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '==',
+  value varchar(253)  NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY groupname (groupname(32))
+);
+
+#
+# Table structure for table 'radgroupreply'
+#
+
+CREATE TABLE radgroupreply (
+  id int(11) unsigned NOT NULL auto_increment,
+  groupname varchar(64) NOT NULL default '',
+  attribute varchar(64)  NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '=',
+  value varchar(253)  NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY groupname (groupname(32))
+);
+
+#
+# Table structure for table 'radreply'
+#
+
+CREATE TABLE radreply (
+  id int(11) unsigned NOT NULL auto_increment,
+  username varchar(64) NOT NULL default '',
+  attribute varchar(64) NOT NULL default '',
+  op char(2) NOT NULL DEFAULT '=',
+  value varchar(253) NOT NULL default '',
+  PRIMARY KEY  (id),
+  KEY username (username(32))
+);
+
+
+#
+# Table structure for table 'radusergroup'
+#
+
+CREATE TABLE radusergroup (
+  username varchar(64) NOT NULL default '',
+  groupname varchar(64) NOT NULL default '',
+  priority int(11) NOT NULL default '1',
+  KEY username (username(32))
+);
+
+#
+# Table structure for table 'radpostauth'
+#
+CREATE TABLE radpostauth (
+  id int(11) NOT NULL auto_increment,
+  username varchar(64) NOT NULL default '',
+  pass varchar(64) NOT NULL default '',
+  reply varchar(32) NOT NULL default '',
+  authdate timestamp NOT NULL,
+  PRIMARY KEY  (id)
+) ENGINE = INNODB;
+
+#
+# Table structure for table 'nas'
+#
+CREATE TABLE nas (
+  id int(10) NOT NULL auto_increment,
+  nasname varchar(128) NOT NULL,
+  shortname varchar(32),
+  type varchar(30) DEFAULT 'other',
+  ports int(5),
+  secret varchar(60) DEFAULT 'secret' NOT NULL,
+  server varchar(64),
+  community varchar(50),
+  description varchar(200) DEFAULT 'RADIUS Client',
+  PRIMARY KEY (id),
+  KEY nasname (nasname)
+);
diff --git a/src/tests/salt-test-server/salt/mysql/setup.sql b/src/tests/salt-test-server/salt/mysql/setup.sql
new file mode 100644 (file)
index 0000000..3b9ec54
--- /dev/null
@@ -0,0 +1,18 @@
+# -*- text -*-
+##
+## admin.sql -- MySQL commands for creating the RADIUS user.
+##
+##     WARNING: You should change 'localhost' and 'radpass'
+##              to something else.  Also update raddb/sql.conf
+##              with the new RADIUS password.
+##
+##     $Id$
+
+#
+#  Create default administrator for RADIUS
+#
+CREATE USER 'radius';
+SET PASSWORD FOR 'radius' = PASSWORD('radpass');
+
+# Need to read when running RADIUS and delete for cleanup
+GRANT ALL ON radius.* TO 'radius';
diff --git a/src/tests/salt-test-server/salt/ntp.sls b/src/tests/salt-test-server/salt/ntp.sls
new file mode 100644 (file)
index 0000000..66434ff
--- /dev/null
@@ -0,0 +1,22 @@
+UTC:
+  timezone.system
+
+ntp_daemon:
+    # Make sure ntp is installed and running
+    pkg:
+{% if grains['os'] == 'CentOS' or grains['os'] == 'Ubuntu' or grains['os'] == 'Debian' %}
+        - name: ntp
+{% elif grains['os'] == 'FreeBSD' %}
+        - name: openntpd
+{% endif %}
+        - installed
+
+# Make sure ntpd is running and enabled (start on boot)
+{% if grains['os'] == 'CentOS' or grains['os'] == 'FreeBSD' %}
+ntpd:
+{% elif grains['os'] == 'Ubuntu' or grains['os'] == 'Debian' %}
+ntp:
+{% endif %}
+    service:
+        - running
+        - enable: True
diff --git a/src/tests/salt-test-server/salt/postgres.sls b/src/tests/salt-test-server/salt/postgres.sls
new file mode 100644 (file)
index 0000000..26fc4e6
--- /dev/null
@@ -0,0 +1,71 @@
+postgresql:
+    # Install postgres and make sure it is running and starts on boot
+    pkg:
+        - installed
+    # Only try to start service after DB has been initialized (will fail otherwise)
+    service:
+        - name: postgresql
+        - running
+        - enable: True
+
+postgres_set_interface:
+    file.sed:
+        - name: /etc/postgresql/9.4/main/postgresql.conf
+        - before: ^\#listen_addresses = 'localhost'
+        - after: listen_addresses = '*'
+
+postgres_password_auth:
+    # Add authentication from anywhere
+    file.append:
+        - name: /etc/postgresql/9.4/main/pg_hba.conf
+        - text:
+            - host    radius      radius        0.0.0.0/0            md5
+
+postgres_restart:
+    # Restart postgres after changes to the config file (reload isn't enough)
+    cmd.wait:
+        - cwd: /
+        - name: service postgresql restart
+        - require:
+            - pkg: postgresql
+            - file: postgres_password_auth
+            - file: postgres_set_interface
+        - watch:
+            - file: /etc/postgresql/9.4/main/postgresql.conf
+            - file: /etc/postgresql/9.4/main/pg_hba.conf
+
+# Copy DB schema file
+/salt/postgres/schema.sql:
+    file.managed:
+        - source: salt://postgres/schema.sql
+        - makedirs: true
+
+# Copy DB setup script
+/salt/postgres/setup.sql:
+    file.managed:
+        - source: salt://postgres/setup.sql
+        - makedirs: true
+
+# Create DB
+create_db:
+    cmd.run:
+        - cwd: /
+        - name: createdb radius
+        - user: postgres
+        - unless: psql --list | grep radius
+
+# Create FreeRADIUS schema
+psql radius < /salt/postgres/schema.sql:
+    cmd.run:
+        - user: postgres
+        - unless: "echo '\\dt public.*' | psql radius | grep radacct;"
+        - require:
+            - file: /salt/postgres/schema.sql
+
+# Setup DB access
+psql radius < /salt/postgres/setup.sql:
+    cmd.run:
+        - user: postgres
+        - unless: "echo '\\du' | psql radius | grep radius"
+        - require:
+            - file: /salt/postgres/setup.sql
diff --git a/src/tests/salt-test-server/salt/postgres/schema.sql b/src/tests/salt-test-server/salt/postgres/schema.sql
new file mode 100644 (file)
index 0000000..c94ee9e
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * $Id$
+ *
+ * Postgresql schema for FreeRADIUS
+ *
+ * All field lengths need checking as some are still suboptimal. -pnixon 2003-07-13
+ *
+ */
+
+/*
+ * Table structure for table 'radacct'
+ *
+ * Note: Column type bigserial does not exist prior to Postgres 7.2
+ *       If you run an older version you need to change this to serial
+ */
+CREATE TABLE radacct (
+       RadAcctId               bigserial PRIMARY KEY,
+       AcctSessionId           text NOT NULL,
+       AcctUniqueId            text NOT NULL UNIQUE,
+       UserName                text,
+       GroupName               text,
+       Realm                   text,
+       NASIPAddress            inet NOT NULL,
+       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,
+       ConnectInfo_stop        text,
+       AcctInputOctets         bigint,
+       AcctOutputOctets        bigint,
+       CalledStationId         text,
+       CallingStationId        text,
+       AcctTerminateCause      text,
+       ServiceType             text,
+       FramedProtocol          text,
+       FramedIPAddress         inet
+);
+-- This index may be useful..
+-- CREATE UNIQUE INDEX radacct_whoson on radacct (AcctStartTime, nasipaddress);
+
+-- 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);
+
+/*
+ * There was WAAAY too many indexes previously. This combo index
+ * should take care of the most common searches.
+ * I have commented out all the old indexes, but left them in case
+ * someone wants them. I don't recomend anywone use them all at once
+ * as they will slow down your DB too much.
+ *  - pnixon 2003-07-13
+ */
+
+/*
+ * create index radacct_UserName on radacct (UserName);
+ * create index radacct_AcctSessionId on radacct (AcctSessionId);
+ * create index radacct_AcctUniqueId on radacct (AcctUniqueId);
+ * create index radacct_FramedIPAddress on radacct (FramedIPAddress);
+ * create index radacct_NASIPAddress on radacct (NASIPAddress);
+ * create index radacct_AcctStartTime on radacct (AcctStartTime);
+ * create index radacct_AcctStopTime on radacct (AcctStopTime);
+*/
+
+
+
+/*
+ * Table structure for table 'radcheck'
+ */
+CREATE TABLE radcheck (
+       id                      serial PRIMARY KEY,
+       UserName                text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '==',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radcheck_UserName on radcheck (UserName,Attribute);
+/*
+ * Use this index if you use case insensitive queries
+ */
+-- create index radcheck_UserName_lower on radcheck (lower(UserName),Attribute);
+
+/*
+ * Table structure for table 'radgroupcheck'
+ */
+CREATE TABLE radgroupcheck (
+       id                      serial PRIMARY KEY,
+       GroupName               text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '==',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radgroupcheck_GroupName on radgroupcheck (GroupName,Attribute);
+
+/*
+ * Table structure for table 'radgroupreply'
+ */
+CREATE TABLE radgroupreply (
+       id                      serial PRIMARY KEY,
+       GroupName               text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '=',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radgroupreply_GroupName on radgroupreply (GroupName,Attribute);
+
+/*
+ * Table structure for table 'radreply'
+ */
+CREATE TABLE radreply (
+       id                      serial PRIMARY KEY,
+       UserName                text NOT NULL DEFAULT '',
+       Attribute               text NOT NULL DEFAULT '',
+       op                      VARCHAR(2) NOT NULL DEFAULT '=',
+       Value                   text NOT NULL DEFAULT ''
+);
+create index radreply_UserName on radreply (UserName,Attribute);
+/*
+ * Use this index if you use case insensitive queries
+ */
+-- create index radreply_UserName_lower on radreply (lower(UserName),Attribute);
+
+/*
+ * Table structure for table 'radusergroup'
+ */
+CREATE TABLE radusergroup (
+       id                      serial PRIMARY KEY,
+       UserName                text NOT NULL DEFAULT '',
+       GroupName               text NOT NULL DEFAULT '',
+       priority                integer NOT NULL DEFAULT 0
+);
+create index radusergroup_UserName on radusergroup (UserName);
+/*
+ * Use this index if you use case insensitive queries
+ */
+-- create index radusergroup_UserName_lower on radusergroup (lower(UserName));
+
+--
+-- Table structure for table 'radpostauth'
+--
+
+CREATE TABLE radpostauth (
+       id                      bigserial PRIMARY KEY,
+       username                text NOT NULL,
+       pass                    text,
+       reply                   text,
+       CalledStationId         text,
+       CallingStationId        text,
+       authdate                timestamp with time zone NOT NULL default now()
+);
+
+/*
+ * Table structure for table 'nas'
+ */
+CREATE TABLE nas (
+       id                      serial PRIMARY KEY,
+       nasname                 text NOT NULL,
+       shortname               text NOT NULL,
+       type                    text NOT NULL DEFAULT 'other',
+       ports                   integer,
+       secret                  text NOT NULL,
+       server                  text,
+       community               text,
+       description             text
+);
+create index nas_nasname on nas (nasname);
diff --git a/src/tests/salt-test-server/salt/postgres/setup.sql b/src/tests/salt-test-server/salt/postgres/setup.sql
new file mode 100644 (file)
index 0000000..6b41aa1
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * admin.sql -- PostgreSQL commands for creating the RADIUS user.
+ *
+ *     WARNING: You should change 'localhost' and 'radpass'
+ *              to something else.  Also update raddb/sql.conf
+ *              with the new RADIUS password.
+ *
+ *     WARNING: This example file is untested.  Use at your own risk.
+ *              Please send any bug fixes to the mailing list.
+ *
+ *     $Id$
+ */
+
+/*
+ *  Create default administrator for RADIUS
+ */
+CREATE USER radius WITH PASSWORD 'radpass';
+
+/* radius user needs ti clean tables in test env */
+GRANT ALL ON ALL TABLES IN SCHEMA public TO radius;
+GRANT SELECT, USAGE ON ALL SEQUENCES IN schema public TO radius;
diff --git a/src/tests/salt-test-server/salt/top.sls b/src/tests/salt-test-server/salt/top.sls
new file mode 100644 (file)
index 0000000..efba703
--- /dev/null
@@ -0,0 +1,7 @@
+base:
+  'test-server':
+    - ntp
+    - mysql
+    - postgres
+    - ldap
+    - iptable
diff --git a/src/tests/salt-test-server/salt_config/master b/src/tests/salt-test-server/salt_config/master
new file mode 100644 (file)
index 0000000..257396a
--- /dev/null
@@ -0,0 +1,12 @@
+root_dir: .
+# pki_dir and cachedir are prefixed with root_dir
+pki_dir: /tmp/
+cachedir: /cache/
+file_roots:
+  base:
+    # salt directory in current working directory
+    - salt
+pillar_roots:
+  base:
+    # pillar directory in current working directory
+    - pillar
diff --git a/src/tests/salt-test-server/salt_config/roster b/src/tests/salt-test-server/salt_config/roster
new file mode 100644 (file)
index 0000000..8958bd1
--- /dev/null
@@ -0,0 +1,4 @@
+test-server:
+    host: 192.168.2.132
+    user: root
+    passwd: root
index 3827842..95b63fd 100644 (file)
@@ -7,7 +7,7 @@
 #  functionality from earlier tests.
 #
 FILES  := rfc.txt errors.txt extended.txt lucent.txt wimax.txt \
-       escape.txt condition.txt xlat.txt vendor.txt
+       escape.txt condition.txt xlat.txt vendor.txt dhcp.txt
 
 #
 #  Create the output directory
@@ -16,13 +16,27 @@ FILES  := rfc.txt errors.txt extended.txt lucent.txt wimax.txt \
 $(BUILD_DIR)/tests/unit:
        @mkdir -p $@
 
+.PHONY: $(BUILD_DIR)/share
+$(BUILD_DIR)/share:
+       @mkdir -p $@
+
+#
+#  We need $INCLUDE in the output file, so we pass 2 parameters to 'echo'
+#  No idea how portable that is...
+#
+$(BUILD_DIR)/share/dictionary: $(top_srcdir)/share/dictionary $(top_srcdir)/share/dictionary.dhcp | $(BUILD_DIR)/share
+       @rm -f $@
+       @for x in $^; do \
+               echo '$$INCLUDE ' "$$x" >> $@; \
+       done
+
 #
 #  Files in the output dir depend on the unit tests
 #
-$(BUILD_DIR)/tests/unit/%: $(DIR)/% $(TESTBINDIR)/radattr | $(BUILD_DIR)/tests/unit
+$(BUILD_DIR)/tests/unit/%: $(DIR)/% $(BUILD_DIR)/bin/radattr $(TESTBINDIR)/radattr $(BUILD_DIR)/share/dictionary | $(BUILD_DIR)/tests/unit
        @echo UNIT-TEST $(notdir $@)
-       @if ! $(TESTBIN)/radattr -D $(top_srcdir)/share $<; then \
-               echo "$(TESTBIN)/radattr -D $(top_srcdir)/share $<"; \
+       @if ! $(TESTBIN)/radattr -D $(BUILD_DIR)/share $<; then \
+               echo "$(TESTBIN)/radattr -D $(BUILD_DIR)/share $<"; \
                exit 1; \
        fi
        @touch $@
index 7d91915..f7d055c 100644 (file)
@@ -207,7 +207,7 @@ data &Framed-IP-Address <= 192.168.0.0/16
 
 # string attributes must be string
 condition User-Name == "bob"
-data &User-Name == 'bob'
+data &User-Name == "bob"
 
 condition User-Name == `bob`
 data &User-Name == `bob`
@@ -279,13 +279,13 @@ condition 0
 data false
 
 condition true && (User-Name == "bob")
-data &User-Name == 'bob'
+data &User-Name == "bob"
 
 condition false && (User-Name == "bob")
 data false
 
 condition false || (User-Name == "bob")
-data &User-Name == 'bob'
+data &User-Name == "bob"
 
 condition true || (User-Name == "bob")
 data true
@@ -318,7 +318,7 @@ condition ("foo" == "%{sql: foo}")
 data foo == "%{sql: foo}"
 
 condition ("foo bar" == "%{sql: foo}")
-data 'foo bar' == "%{sql: foo}"
+data "foo bar" == "%{sql: foo}"
 
 condition ("foo" == "bar")
 data false
@@ -332,7 +332,7 @@ data false
 #  in it, so it reverts back to a literal.
 #
 condition (&User-Name == "bob")
-data &User-Name == 'bob'
+data &User-Name == "bob"
 
 condition (&User-Name == "%{sql: blah}")
 data &User-Name == "%{sql: blah}"
@@ -425,7 +425,7 @@ data ERROR offset 1 Cannot use attribute reference on right side of condition
 #  is on the LHS of the condition.
 #
 condition <string>"foo" == &User-Name
-data &User-Name == 'foo'
+data &User-Name == "foo"
 
 condition <integer>"%{expr: 1 + 1}" < &NAS-Port
 data &NAS-Port > "%{expr: 1 + 1}"
@@ -465,28 +465,34 @@ 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'
+data &Tunnel-Password:0 == "Hello"
 
 condition &Tunnel-Password:1 == "Hello"
+data &Tunnel-Password:1 == "Hello"
+
+#
+# Single quoted strings are left as-is
+#
+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'
+data &User-Name[0] == "bob"
 
 condition &User-Name[1] == "bob"
-data &User-Name[1] == 'bob'
+data &User-Name[1] == "bob"
 
 condition &User-Name[n] == "bob"
-data &User-Name[n] == 'bob'
+data &User-Name[n] == "bob"
 
 condition &Tunnel-Password:1[0] == "Hello"
-data &Tunnel-Password:1[0] == 'Hello'
+data &Tunnel-Password:1[0] == "Hello"
 
 condition &Tunnel-Password:1[3] == "Hello"
-data &Tunnel-Password:1[3] == 'Hello'
+data &Tunnel-Password:1[3] == "Hello"
 
 #
 #  This is allowed for pass2-fixups.  Foo-Bar MAY be an attribute.
@@ -586,14 +592,14 @@ condition request:Foo+Bar == request:Foo+Bar
 data true
 
 condition &request:Foo+Bar == 'request:Foo+Bar'
-data ERROR offset 12 Unexpected text after attribute name
+data ERROR offset 12 Unexpected text after unknown attr
 
 condition 'request:Foo+d' == &request:Foo+Bar
-data ERROR offset 31 Unexpected text after attribute name
+data ERROR offset 31 Unexpected text after unknown attr
 
 #  Attribute tags are not allowed for unknown attributes
 condition &request:FooBar:0 == &request:FooBar
-data ERROR offset 15 Unexpected text after attribute name
+data ERROR offset 15 Unexpected text after unknown attr
 
 condition &not-a-list:User-Name == &not-a-list:User-Name
 data ERROR offset 1 Invalid list qualifier
@@ -619,3 +625,40 @@ data &FreeRADIUS-Proxied-To == 127.0.0.1/32
 
 condition &FreeRADIUS-Attr-1 == 127.0.0.1
 data &FreeRADIUS-Proxied-To == 127.0.0.1/32
+
+#
+#  Escape the backslashes correctly
+#  And print them correctly
+#
+condition &User-Name =~ /@|\\/
+data &User-Name =~ /@|\\/
+
+condition &User-Name == '\\'
+data &User-Name == '\\'
+
+condition &User-Name !~ /^foo\nbar$/
+data !&User-Name =~ /^foo\nbar$/
+
+condition &User-Name == "@|\\"
+data &User-Name == "@|\\"
+
+condition &User-Name != "foo\nbar"
+data !&User-Name == "foo\nbar"
+
+condition User-Name =~ /^([^\\]*)\\(.*)$/
+data &User-Name =~ /^([^\\]*)\\(.*)$/
+
+#
+#  We require explicit casts
+#
+condition 192.168.0.0/16 > 192.168.1.2
+data false
+
+condition <ipv4prefix>192.168.0.0/16 > 192.168.1.2
+data true
+
+condition <ipv4prefix>&NAS-IP-Address == 192.168.0.0/24
+data <ipv4prefix>&NAS-IP-Address == 192.168.0.0/24
+
+condition <ipv4prefix>192.168.0.0/24 > &NAS-IP-Address
+data <ipv4prefix>192.168.0.0/24 > &NAS-IP-Address
diff --git a/src/tests/unit/dhcp.txt b/src/tests/unit/dhcp.txt
new file mode 100644 (file)
index 0000000..11ddd86
--- /dev/null
@@ -0,0 +1,22 @@
+#
+#  Test vectors for DHCP attributes
+#
+
+encode-dhcp DHCP-Subnet-Mask = 255.255.0.0
+data 01 04 ff ff 00 00
+
+decode-dhcp -
+data DHCP-Subnet-Mask = 255.255.0.0
+
+#
+#  A long one... with a weird DHCP-specific vendor ID.
+#
+decode-dhcp 3501013d0701001ceaadac1e37070103060f2c2e2f3c094d5346545f495054565232011c4c41424f4c54322065746820312f312f30312f30312f31302f312f3209120000197f0d050b4c4142373336304f4c5432
+data DHCP-Message-Type = DHCP-Discover, DHCP-Client-Identifier = 0x01001ceaadac1e, DHCP-Parameter-Request-List = DHCP-Subnet-Mask, DHCP-Parameter-Request-List = DHCP-Router-Address, DHCP-Parameter-Request-List = DHCP-Domain-Name-Server, DHCP-Parameter-Request-List = DHCP-Domain-Name, DHCP-Parameter-Request-List = DHCP-NETBIOS-Name-Servers, DHCP-Parameter-Request-List = DHCP-NETBIOS-Node-Type, DHCP-Parameter-Request-List = DHCP-NETBIOS, DHCP-Vendor-Class-Identifier = 0x4d5346545f49505456, DHCP-Relay-Circuit-Id = 0x4c41424f4c54322065746820312f312f30312f30312f31302f312f32, DHCP-Vendor-Specific-Information = 0x0000197f0d050b4c4142373336304f4c5432
+
+
+encode-dhcp DHCP-Agent-Circuit-Id = 0xabcdef, DHCP-Relay-Remote-Id = 0x010203040506
+data 52 0d 01 03 ab cd ef 02 06 01 02 03 04 05 06
+
+decode-dhcp -
+data DHCP-Relay-Circuit-Id = 0xabcdef, DHCP-Relay-Remote-Id = 0x010203040506
index 5f96f92..b65e1b7 100644 (file)
@@ -30,6 +30,18 @@ data true
 condition 0x22 == '"'
 data true
 
+condition '\'' == "'"
+data true
+
+condition '\\' == "\\"
+data true
+
+#
+#  The first string is \ + x
+#
+condition '\x' == "x"
+data false
+
 # embedded zeros are OK
 condition "a\000a" == 0x610061
 data true
index 9ba1380..8b0e3a2 100644 (file)
@@ -69,6 +69,9 @@ data Attr-245.1 = 0x787878787878787878787878787878787878787878787878787878787878
 raw 245.26.1.6 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccc13456789
 data f5 ff 1a 80 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb f5 17 1a 00 bb bb bb bb bb cc cc cc cc cc cc cc cc cc cc 13 45 67 89
 
+decode f5 ff 1a 80 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb f5 17 1a 00 bb bb bb bb bb cc cc cc cc cc cc cc cc cc cc 13 45 67 89
+data Attr-245.26.1.6 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccc13456789
+
 # Same as above, but the first attribute doesn't have
 # the "continuation" bit set.
 decode f5 ff 1a 00 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb f5 17 1a 00 bb bb bb bb bb cc cc cc cc cc cc cc cc cc cc 13 45 67 89
@@ -77,7 +80,7 @@ data Attr-245.26.1.6 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 
 # again, but the second one attr is not an extended attr
 decode f5 ff 1a 80 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb 01 05 62 6f 62
-data Attr-245.26.1.6 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, User-Name = 'bob'
+data Attr-245.26.1.6 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, User-Name = "bob"
 
 # No data means that the attribute is an "invalid attribute"
 decode f5 04 01 00
index 21d3e52..d89b174 100644 (file)
 #  The output data is the hex version of the encoded attribute.
 #
 
-encode User-Name = 'bob'
+encode User-Name = "bob"
 data 01 05 62 6f 62
 
 decode -
-data User-Name = 'bob'
+data User-Name = "bob"
 
 decode 01 05 62 6f 62
-data User-Name = 'bob'
+data User-Name = "bob"
 
 #
 #  The Type/Length is OK, but the attribute data is of the wrong size.
@@ -135,6 +135,7 @@ data Framed-IPv6-Prefix = 11:22:33:44:55:66:77:88/128
 attribute Framed-IPv6-Prefix = *
 data Framed-IPv6-Prefix = ::/128
 
+$INCLUDE tunnel.txt
 $INCLUDE errors.txt
 $INCLUDE extended.txt
 $INCLUDE lucent.txt
diff --git a/src/tests/unit/tunnel.txt b/src/tests/unit/tunnel.txt
new file mode 100644 (file)
index 0000000..da0cb31
--- /dev/null
@@ -0,0 +1,87 @@
+#
+#  We can't look at the data here, because the encoded Tunnel-Password has a 2 byte
+#  random salt
+#
+encode Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxabc"
+decode -
+data Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxabc"
+
+encode Tunnel-Password:0 = "0"
+decode -
+data Tunnel-Password:0 = "0"
+
+encode Tunnel-Password:0 = "01"
+decode -
+data Tunnel-Password:0 = "01"
+
+encode Tunnel-Password:0 = "012"
+decode -
+data Tunnel-Password:0 = "012"
+
+encode Tunnel-Password:0 = "0123"
+decode -
+data Tunnel-Password:0 = "0123"
+
+encode Tunnel-Password:0 = "01234"
+decode -
+data Tunnel-Password:0 = "01234"
+
+encode Tunnel-Password:0 = "012345"
+decode -
+data Tunnel-Password:0 = "012345"
+
+encode Tunnel-Password:0 = "0123456"
+decode -
+data Tunnel-Password:0 = "0123456"
+
+encode Tunnel-Password:0 = "01234567"
+decode -
+data Tunnel-Password:0 = "01234567"
+
+encode Tunnel-Password:0 = "012345678"
+decode -
+data Tunnel-Password:0 = "012345678"
+
+encode Tunnel-Password:0 = "0123456789"
+decode -
+data Tunnel-Password:0 = "0123456789"
+
+encode Tunnel-Password:0 = "0123456789a"
+decode -
+data Tunnel-Password:0 = "0123456789a"
+
+encode Tunnel-Password:0 = "0123456789ab"
+decode -
+data Tunnel-Password:0 = "0123456789ab"
+
+encode Tunnel-Password:0 = "0123456789abc"
+decode -
+data Tunnel-Password:0 = "0123456789abc"
+
+encode Tunnel-Password:0 = "0123456789abcd"
+decode -
+data Tunnel-Password:0 = "0123456789abcd"
+
+encode Tunnel-Password:0 = "0123456789abcde"
+decode -
+data Tunnel-Password:0 = "0123456789abcde"
+
+encode Tunnel-Password:0 = "0123456789abcdef"
+decode -
+data Tunnel-Password:0 = "0123456789abcdef"
+
+#
+#  We can't look at the data here, because the encoded Tunnel-Password has a 2 byte
+#  random salt
+#
+encode Tunnel-Password:0 := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+decode -
+data Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+#
+#  1 octet for the tag.  2 octets for salt.  One octet for encrypted length.
+#  249 octets left for real data.
+#
+encode Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789ab"
+decode -
+data Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789"
index f54e429..4f05c52 100644 (file)
@@ -2,7 +2,7 @@ encode SN-VPN-Name = "foo"
 data 1a 0d 00 00 1f e4 00 02 00 07 66 6f 6f
 
 decode 1a 0d 00 00 1f e4 00 02 00 07 66 6f 6f
-data SN-VPN-Name = 'foo'
+data SN-VPN-Name = "foo"
 
 encode USR-Event-Id = 1234
 data 1a 0e 00 00 01 ad 00 00 bf be 00 00 04 d2
@@ -14,10 +14,10 @@ decode 1a 15 00 00 4e 20 01 0f 6c 69 74 68 69 61 73 70 72 69 6e 67 73
 data Attr-26.20000.1 = 0x6c6974686961737072696e6773
 
 decode 1a 2e 00 00 00 2b 1c 02 01 06 00 00 00 00 3c 20 31 35 35 2e 34 2e 31 32 2e 31 30 30 20 30 30 3a 30 30 3a 30 30 3a 30 30 3a 30 30 3a 30 30
-data 3Com-User-Access-Level = 3Com-Visitor, 3Com-Ip-Host-Addr = '155.4.12.100 00:00:00:00:00:00'
+data 3Com-User-Access-Level = 3Com-Visitor, 3Com-Ip-Host-Addr = "155.4.12.100 00:00:00:00:00:00"
 
 decode 1a 2c 00 00 00 2b 01 06 00 00 00 00 3c 20 31 35 35 2e 34 2e 31 32 2e 31 30 30 20 30 30 3a 30 30 3a 30 30 3a 30 30 3a 30 30 3a 30 30
-data 3Com-User-Access-Level = 3Com-Visitor, 3Com-Ip-Host-Addr = '155.4.12.100 00:00:00:00:00:00'
+data 3Com-User-Access-Level = 3Com-Visitor, 3Com-Ip-Host-Addr = "155.4.12.100 00:00:00:00:00:00"
 
 encode Vendor-Specific = 0xabcdef
 data Must use 'Attr-26 = ...' instead of 'Vendor-Specific = ...'
@@ -27,3 +27,12 @@ data 1a 09 00 00 00 09 ab cd ef
 
 attribute Attr-26 = 0x00000009abcdef
 data Attr-26 = 0x00000009abcdef
+attribute Ascend-Data-Filter = 0x01010100010203040a0b0c0d05200600000504d2020200000000000000000000
+data Ascend-Data-Filter = "ip in forward srcip 1.2.3.4/5 dstip 10.11.12.13/32 tcp srcport = 5 dstport = 1234"
+
+encode -
+data 1a 28 00 00 02 11 f2 22 01 01 01 00 01 02 03 04 0a 0b 0c 0d 05 20 06 00 00 05 04 d2 02 02 00 00 00 00 00 00 00 00 00 00
+
+decode 1a2800000211f22201010100010203040a0b0c0d05200600000504d2020200000000000000000000
+data Ascend-Data-Filter = "ip in forward srcip 1.2.3.4/5 dstip 10.11.12.13/32 tcp srcport = 5 dstport = 1234"
+
index a7e77dc..191b37e 100644 (file)
@@ -5,7 +5,7 @@ encode WiMAX-Release = "1.0"
 data 1a 0e 00 00 60 b5 01 08 00 01 05 31 2e 30
 
 decode -
-data WiMAX-Release = '1.0'
+data WiMAX-Release = "1.0"
 
 encode WiMAX-Accounting-Capabilities = 1
 data 1a 0c 00 00 60 b5 01 06 00 02 03 01
@@ -17,7 +17,7 @@ encode WiMAX-Release = "1.0", WiMAX-Accounting-Capabilities = 1
 data 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 03 01
 
 decode -
-data WiMAX-Release = '1.0', WiMAX-Accounting-Capabilities = IP-Session-Based
+data WiMAX-Release = "1.0", WiMAX-Accounting-Capabilities = IP-Session-Based
 
 encode -
 data 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 03 01
@@ -31,6 +31,9 @@ data 1a 14 00 00 60 b5 54 0e 00 09 0b 04 03 01 05 06 04 04 1a 99
 decode -
 data WiMAX-PFDv2-Classifier-Direction = 1, WiMAX-PFDv2-Src-Port = 6809
 
+decode 1a 11 00 00 60 b5 54 0b 00 09 08 05 06 04 04 1a 99
+data WiMAX-PFDv2-Src-Port = 6809
+
 # 26.24757.89.9.4 has the correct length.
 # 26.24757.89.9.5 has the correct length.
 # 26.24757.89.9.5.4 has the wrong length.
@@ -112,4 +115,31 @@ data Attr-26.24757.84.9.9.3 = 0x010237, WiMAX-PFDv2-Eth-Priority-Range-High = 84
 #  Simple test for continued attributes
 #
 decode 1a 0e 00 00 60 b5 01 08 80 01 05 31 2e 30 1a 0c 00 00 60 b5 01 06 00 02 03 00
-data WiMAX-Release = '1.0', WiMAX-Accounting-Capabilities = No-Accounting
+data WiMAX-Release = "1.0", WiMAX-Accounting-Capabilities = No-Accounting
+
+#
+#  See if encoding multiple attributes works
+#
+encode WiMAX-Packet-Data-Flow-Id := 32, WiMAX-Service-Data-Flow-ID := 32, WiMAX-Service-Profile-ID := 32
+data 1a 17 00 00 60 b5 1c 11 00 01 04 00 20 02 04 00 20 03 06 00 00 00 20
+
+encode WiMAX-Packet-Data-Flow-Id := 33, WiMAX-Service-Data-Flow-ID := 33, WiMAX-Service-Profile-ID := 33
+data 1a 17 00 00 60 b5 1c 11 00 01 04 00 21 02 04 00 21 03 06 00 00 00 21
+
+encode WiMAX-Packet-Data-Flow-Id := 32, WiMAX-Service-Data-Flow-ID := 32, WiMAX-Service-Profile-ID := 32, WiMAX-Packet-Data-Flow-Id := 33, WiMAX-Service-Data-Flow-ID := 33, WiMAX-Service-Profile-ID := 33
+data 1a 25 00 00 60 b5 1c 1f 00 01 04 00 20 02 04 00 20 03 06 00 00 00 20 01 04 00 21 02 04 00 21 03 06 00 00 00 21
+
+encode WiMAX-Packet-Data-Flow-Id := 32, WiMAX-Service-Data-Flow-ID := 32, WiMAX-Service-Profile-ID := 32, WiMAX-Packet-Data-Flow-Id := 33, WiMAX-Service-Data-Flow-ID := 33, WiMAX-Service-Profile-ID := 33, Session-Timeout := 7200
+data 1a 25 00 00 60 b5 1c 1f 00 01 04 00 20 02 04 00 20 03 06 00 00 00 20 01 04 00 21 02 04 00 21 03 06 00 00 00 21 1b 06 00 00 1c 20
+
+encode Acct-Interim-Interval := 3600, WiMAX-Packet-Data-Flow-Id := 32, WiMAX-Service-Data-Flow-ID := 32, WiMAX-Service-Profile-ID := 32, WiMAX-Packet-Data-Flow-Id := 33, WiMAX-Service-Data-Flow-ID := 33, WiMAX-Service-Profile-ID := 33, Session-Timeout := 7200
+data 55 06 00 00 0e 10 1a 25 00 00 60 b5 1c 1f 00 01 04 00 20 02 04 00 20 03 06 00 00 00 20 01 04 00 21 02 04 00 21 03 06 00 00 00 21 1b 06 00 00 1c 20
+
+encode WiMAX-Packet-Data-Flow-Id := 32, WiMAX-Service-Data-Flow-ID := 32, WiMAX-Service-Profile-ID := 32, Session-Timeout := 7200, WiMAX-Packet-Data-Flow-Id := 33, WiMAX-Service-Data-Flow-ID := 33, WiMAX-Service-Profile-ID := 33
+data 1a 17 00 00 60 b5 1c 11 00 01 04 00 20 02 04 00 20 03 06 00 00 00 20 1b 06 00 00 1c 20 1a 17 00 00 60 b5 1c 11 00 01 04 00 21 02 04 00 21 03 06 00 00 00 21
+
+encode WiMAX-Capability = 0x01ff45454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545454545040301
+data 1a ff 00 00 60 b5 01 f9 80 01 ff 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 1a 15 00 00 60 b5 01 0f 00 45 45 45 45 45 45 45 45 45 04 03 01
+
+decode -
+data WiMAX-Release = "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", WiMAX-Idle-Mode-Notification-Cap = Supported
index 8306b9b..5dc4893 100644 (file)
@@ -128,3 +128,15 @@ data /([A-Z0-9\-]*)_%{Calling-Station-Id}/
 
 xlat %{length:1 + 2
 data ERROR offset 14 'Missing closing brace at end of string'
+
+xlat "%t\tfoo"
+data "%t\tfoo"
+
+xlat "%t\t%{Client-IP-Address}"
+data "%t\t%{Client-IP-Address}"
+
+xlat "foo %{test}"
+data ERROR offset 11 'Missing content in expansion'
+
+xlat "foo %{test:foo}"
+data "foo %{test:foo}"
index 0b68375..b6485f2 100644 (file)
@@ -1,5 +1,5 @@
 Name:         freeradius-server
-Version: 3.0.7
+Version: 3.0.10
 Release:      0
 License:      GPLv2 ; LGPLv2.1
 Group:        Productivity/Networking/Radius/Servers