diff -Nru exim4-4.84/ACKNOWLEDGMENTS exim4-4.86~RC4/ACKNOWLEDGMENTS --- exim4-4.84/ACKNOWLEDGMENTS 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/ACKNOWLEDGMENTS 2015-06-27 17:01:28.000000000 +0200 @@ -350,7 +350,7 @@ Tom Kistner DKIM. Content scanning. SPA. Todd Lyons Nigel Metheringham Transitioning out of Default Victim status. -Phil Pennock Release Coordinator. Breaks lots of things. +Phil Pennock Mostly idle; some security bits still. David Woodhouse Dynamic modules. Security. @@ -449,6 +449,7 @@ Samuel Thibault Patch fixing IPv6 interface address detection on Hurd Martin Tscholak Reported issue with TLS anonymous ciphersuites Stephen Usher Patch fixing use of Oracle's LDAP libraries on Solaris +Jasper Wallace Patch for LibreSSL compatibility Holger Weiß Patch leting ${run} return more data than OS pipe buffer size Moritz Wilhelmy Pointed out PCRE_PRERELEASE glitch diff -Nru exim4-4.84/debian/changelog exim4-4.86~RC4/debian/changelog --- exim4-4.84/debian/changelog 2015-04-03 14:16:43.000000000 +0200 +++ exim4-4.86~RC4/debian/changelog 2015-07-06 12:09:44.000000000 +0200 @@ -1,3 +1,161 @@ +exim4 (4.86~RC4-1ubuntu1) wily; urgency=low + + * Merge from Debian unstable. (LP: #1166671) Remaining changes: + - debian/control, debian/patches/fix_smtp_banner.patch: + + Show Ubuntu distribution in SMTP banner. + + Build-Depends on lsb-release. + + -- Artur Rona Mon, 06 Jul 2015 12:09:36 +0200 + +exim4 (4.86~RC4-1) unstable; urgency=medium + + * unexport/undefine TZ in debian/rules for reproducible build. It would be + used as default value for TIMEZONE_DEFAULT. + * New upstream version. + + Unfuzz 31_eximmanpage.dpatch. + + -- Andreas Metzler Mon, 29 Jun 2015 07:43:19 +0200 + +exim4 (4.86~RC3-2) unstable; urgency=medium + + * Upload to unstable. + + -- Andreas Metzler Tue, 23 Jun 2015 19:11:19 +0200 + +exim4 (4.86~RC3-1) experimental; urgency=medium + + * Don't provide default-mta on Ubuntu and Ubuntu-derivatives. See LP-bug + 1166671. + * New upstream version. + + -- Andreas Metzler Mon, 22 Jun 2015 20:39:11 +0200 + +exim4 (4.86~RC2-1) experimental; urgency=medium + + * Drop nowadays unneeded XS-Testsuite: autopkgtest in debian/control + (Thanks, lintian). + * New upstream version: + +Drop included patches. + (-72_0001-Guard-routing-against-a-null-deref.-Bug-1639.patch, + 72_0002-Spamd-add-missing-initialiser.-Rspamd-mode-was-incor.patch, + 72_0003-DSN-fix-null-deref-when-bounce-is-due-to-conn-timeou.patch, + 72_0004-Content-scan-Use-ETIMEDOUT-not-ETIME-as-having-bette.patch) + * Sync Debian config with upstream default config: + + Set prdr_enable. + + Add +smtp_protocol_error +smtp_syntax_error +tls_certificate_verified to + log_selector option value. + + -- Andreas Metzler Wed, 17 Jun 2015 19:49:58 +0200 + +exim4 (4.86~RC1-3) experimental; urgency=medium + + * Get time and date of latest debian/changelog entry and patch exim(on) to + use these instead of __DATE__ and __TIME__. + * Pull 72_0004-Content-scan-Use-ETIMEDOUT-not-ETIME-as-having-bette.patch + from GIT to fix FTBFS on kfreebsd. + + -- Andreas Metzler Sat, 13 Jun 2015 15:22:47 +0200 + +exim4 (4.86~RC1-2) experimental; urgency=medium + + * Pull three post-release fixes from upstream GIT. (null pointer + derefencing, and spam scanning defaulting to rspam mode) + + 72_0001-Guard-routing-against-a-null-deref.-Bug-1639.patch + + 72_0002-Spamd-add-missing-initialiser.-Rspamd-mode-was-incor.patch + + 72_0003-DSN-fix-null-deref-when-bounce-is-due-to-conn-timeou.patch + + -- Andreas Metzler Sun, 07 Jun 2015 07:26:13 +0200 + +exim4 (4.86~RC1-1) experimental; urgency=medium + + * New upstream release. + + Drop 84_Fix-truncation-of-items-in-headers_remove-lists-this.patch, + refresh patches. + + Update EDITME*, enable AUTH_TLS for -heavy. + + Sync Debian config with upstream default config, rfc1413 calls are now + disabled by default. + + Uses MIME format bounce messages (RFC 3461). Closes: #230284,#400741 + + The spamd_address main option now supports an optional timeout value per + server (tmo=timespec), it defaults two 2 minutes. Closes: #297915 + + spamd_address also accepts hostnames and IPv6 addresses. Closes: #751687 + + log reason for defer, on a hostlist dns-lookup temporary error. + Closes: #670035 + + -- Andreas Metzler Sat, 06 Jun 2015 15:41:33 +0200 + +exim4 (4.85-3) unstable; urgency=medium + + * Upload to unstable. + + -- Andreas Metzler Tue, 28 Apr 2015 19:34:16 +0200 + +exim4 (4.85-2) experimental; urgency=medium + + * Merge from unstable 4.84-8. + + Tighten dependency of exim4 on exim4-base to (>= ${source:Version}) and + (<< ${source:Version}.1), at least source version, but not the next + sourceful upload. Closes: #777246 + + Pull 84_Fix-truncation-of-items-in-headers_remove-lists-this.patch from + upstream GIT which fixes breakage of string-expansion in headers_remove + commands. (Thanks Gordon Dickens, for the pointer.) - + 83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch not added + here since it already part of 4.85. + + -- Andreas Metzler Sat, 21 Feb 2015 15:38:47 +0100 + +exim4 (4.85-1) experimental; urgency=medium + + * exim4-config_files.5: Escape dots in regex. (Thanks, ael) + * New upstream version. + + -- Andreas Metzler Tue, 13 Jan 2015 18:48:45 +0100 + +exim4 (4.85~RC4-1) experimental; urgency=medium + + * update-exim4.conf: + + Drop unused variable UPEX4C_internal_tmp. + + Use tempfile(1) if the generated file will not be written to + /var/lib/exim4/. + + Add --check option. + * init-script: On restart use update-exim4.conf --check before stopping the + daemon. (This is a no-op with systemd since its sysv compat layer + translates "foo restart" into "foo stop" "foo start" instead of using the + init scripts restart target.) + * Handle _RC in watchfile with uversionmangle. + * New upstream version. + + Stop repacking source, rfcs have been dropped. + + -- Andreas Metzler Wed, 31 Dec 2014 14:24:35 +0100 + +exim4 (4.85~RC3+dfsg-1) experimental; urgency=medium + + * New upstream version. + + -- Andreas Metzler Thu, 18 Dec 2014 19:07:59 +0100 + +exim4 (4.85~RC2+dfsg-1) experimental; urgency=medium + + * New upstream version. + * Unfuzz patches: 50_localscan_dlopen.dpatch 67_unnecessaryCopt.diff + 70_remove_exim-users_references.dpatch. + + -- Andreas Metzler Mon, 01 Dec 2014 18:54:17 +0100 + +exim4 (4.85~RC1+dfsg-1) experimental; urgency=medium + + * Unset message_prefix/message_sufix in maildrop_pipe transport. Maildrop + neither expects a mbox-style From nor an empty line add the end. (Thanks, + Edward Betts) Closes: #769396 + * Change the init script's restart order from { regenerate_config; stop; + start ; } to { stop; regenerate_config; start ; }. (Thanks, Jakub Warmuz) + Closes: #768874 + * New upstream version. + + Unfuzz 66_enlarge-dh-parameters-size.dpatch + + Drop 80_mime_empty_charset.diff. + * Remove rfc from upstream source and repack it. + + -- Andreas Metzler Tue, 18 Nov 2014 19:28:20 +0100 + exim4 (4.84-8ubuntu1) vivid; urgency=low * Merge from Debian unstable. (LP: #1434300) Remaining changes: @@ -4939,3 +5097,4 @@ -- Mark Baker Mon, 4 Mar 2002 23:04:52 +0000 + diff -Nru exim4-4.84/debian/control exim4-4.86~RC4/debian/control --- exim4-4.84/debian/control 2015-04-03 14:16:43.000000000 +0200 +++ exim4-4.86~RC4/debian/control 2015-07-06 11:32:06.000000000 +0200 @@ -15,7 +15,6 @@ libident-dev, libdb5.3-dev, libxmu-dev, libxt-dev, libxext-dev, libx11-dev, libxaw7-dev, libpq-dev, libmysqlclient-dev | libmysqlclient15-dev, libsqlite3-dev, libperl-dev, libgnutls28-dev, libsasl2-dev, lsb-release -XS-Testsuite: autopkgtest Package: exim4-base Architecture: any @@ -94,7 +93,8 @@ Package: exim4-daemon-light Architecture: any -Provides: mail-transport-agent, exim4-localscanapi-1.0, exim4-localscanapi-1.1 +Provides: mail-transport-agent, exim4-localscanapi-1.0, exim4-localscanapi-1.1, + ${dist:Provides:exim4-daemon-light} Conflicts: mail-transport-agent Replaces: mail-transport-agent, exim4-base (<= 4.61-1) Depends: exim4-base (>= ${Upstream-Version}), ${shlibs:Depends}, ${misc:Depends} diff -Nru exim4-4.84/debian/copyright exim4-4.86~RC4/debian/copyright --- exim4-4.84/debian/copyright 2014-07-14 14:08:17.000000000 +0200 +++ exim4-4.86~RC4/debian/copyright 2015-06-23 19:10:26.000000000 +0200 @@ -55,7 +55,7 @@ ----------------------------------------------------------------- -exim is copyright (c) 1999 University of Cambridge. +exim is copyright (c) 1999 - 2015 University of Cambridge. The original licence is as follows (from the file NOTICE in the upstream distribution); a copy of the GNU GPL version 2 is available in @@ -65,7 +65,7 @@ THE EXIM MAIL TRANSFER AGENT ---------------------------- -Copyright (c) 2002 University of Cambridge +Copyright (c) 2004 University of Cambridge 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 @@ -83,7 +83,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. UNSOLICITED EMAIL @@ -101,14 +101,6 @@ A number of pieces of external code are included in the Exim distribution. - . Regular expressions are supported in the main Exim program and in the - Exim monitor using the freely-distributable PCRE library, copyright (c) - 2003 University of Cambridge. The source is distributed in the directory - src/pcre. However, this is a cut-down version of PCRE. If you want to use - the PCRE library in other programs, you should obtain and install the - full version from ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre. - - . Support for the cdb (Constant DataBase) lookup method is provided by code contributed by Nigel Metheringham of Planet Online Ltd. which contains the following statements: @@ -222,11 +214,6 @@ -- Philip Hazel University of Cambridge Computing Service, -ph10@cus.cam.ac.uk Cambridge, England. Phone: +44 1223 334714. ------------------------------------------------------------------ - - - ----------------------------------------------------------------- src/pdkim/* diff -Nru exim4-4.84/debian/debconf/conf.d/main/01_exim4-config_listmacrosdefs exim4-4.86~RC4/debian/debconf/conf.d/main/01_exim4-config_listmacrosdefs --- exim4-4.84/debian/debconf/conf.d/main/01_exim4-config_listmacrosdefs 2012-09-23 12:07:23.000000000 +0200 +++ exim4-4.86~RC4/debian/debconf/conf.d/main/01_exim4-config_listmacrosdefs 2015-06-23 19:10:26.000000000 +0200 @@ -96,5 +96,5 @@ # always log tls_peerdn as we use TLS for outgoing connects by default .ifndef MAIN_LOG_SELECTOR -MAIN_LOG_SELECTOR = +tls_peerdn +MAIN_LOG_SELECTOR = +smtp_protocol_error +smtp_syntax_error +tls_certificate_verified +tls_peerdn .endif diff -Nru exim4-4.84/debian/debconf/conf.d/main/02_exim4-config_options exim4-4.86~RC4/debian/debconf/conf.d/main/02_exim4-config_options --- exim4-4.84/debian/debconf/conf.d/main/02_exim4-config_options 2014-07-14 14:08:17.000000000 +0200 +++ exim4-4.86~RC4/debian/debconf/conf.d/main/02_exim4-config_options 2015-06-23 19:10:26.000000000 +0200 @@ -91,18 +91,26 @@ primary_hostname = MAIN_HARDCODE_PRIMARY_HOSTNAME .endif -# The settings below, which are actually the same as the defaults in the -# code, cause Exim to make RFC 1413 (ident) callbacks for all incoming SMTP -# calls. You can limit the hosts to which these calls are made, and/or change -# the timeout that is used. If you set the timeout to zero, all RFC 1413 calls -# are disabled. RFC 1413 calls are cheap and can provide useful information -# for tracing problem messages, but some hosts and firewalls are -# misconfigured to drop the requests instead of either answering or -# rejecting them. This can result in a timeout instead of an immediate refused -# connection, leading to delays on starting up SMTP sessions. (The default was -# reduced from 30s to 5s for release 4.61.) -# rfc1413_hosts = * -# rfc1413_query_timeout = 5s +# The settings below cause Exim to make RFC 1413 (ident) callbacks +# for all incoming SMTP calls. You can limit the hosts to which these +# calls are made, and/or change the timeout that is used. If you set +# the timeout to zero, all RFC 1413 calls are disabled. RFC 1413 calls +# are cheap and can provide useful information for tracing problem +# messages, but some hosts and firewalls have problems with them. +# This can result in a timeout instead of an immediate refused +# connection, leading to delays on starting up SMTP sessions. +# (The default was reduced from 30s to 5s for release 4.61. and to +# disabled for release 4.86) +# +#rfc1413_hosts = * +#rfc1413_query_timeout = 5s + + +# Enable an efficiency feature. We advertise the feature; clients +# may request to use it. For multi-recipient mails we then can +# reject or accept per-user after the message is received. +# +prdr_enable = true # When using an external relay tester (such as rt.njabl.org and/or the # currently defunct relay-test.mail-abuse.org, the test may be aborted diff -Nru exim4-4.84/debian/debconf/update-exim4.conf exim4-4.86~RC4/debian/debconf/update-exim4.conf --- exim4-4.84/debian/debconf/update-exim4.conf 2014-12-21 14:04:55.000000000 +0100 +++ exim4-4.86~RC4/debian/debconf/update-exim4.conf 2015-05-18 20:42:37.000000000 +0200 @@ -29,12 +29,13 @@ --removecomments - Remove comment lines -o|--output file - write output to file instead of ${UPEX4C_outputfile} -d|--confdir directory - read input from given directory instead of ${UPEX4C_confdir} + --check - Test generated file for validity and remove it again. EOF } ## Parse commandline TEMP=$(getopt -n update-exim4.conf \ - -l keepcomments,removecomments,output:,confdir:,help,verbose -- \ + -l check,keepcomments,removecomments,output:,confdir:,help,verbose -- \ +o:d:vh "$@") if test "$?" != 0; then @@ -58,6 +59,9 @@ --removecomments) UPEX4C_comments=no ;; + --check) + UPEX4C_check=yes + ;; -o|--output) shift UPEX4C_outputfile="$1" @@ -97,6 +101,14 @@ exit 1 fi + +UPEX4C_autoconfigfile=/var/lib/exim4/config.autogenerated +if [ "$(dirname ${UPEX4C_outputfile})" = "/var/lib/exim4" ] ; then + UPEX4C_tmp="${UPEX4C_outputfile}.tmp" +else + UPEX4C_tmp="$(tempfile -m600 -p ex4)" +fi + lowerpipe() { tr 'A-Z' 'a-z' } @@ -152,9 +164,6 @@ local_domains="$(echo @:localhost:"${dc_other_hostnames}" | \ sed -e 's/[;: ]*$//' -e 's/ *//' -e 's/;/:/g')" -UPEX4C_internal_tmp="$(tempfile -m600 -p ex4)" - -trap "rm -f ${UPEX4C_internal_tmp}" EXIT INT TERM # run-parts emulation, stolen from Branden's /etc/X11/Xsession # Addition: Use file.rul instead if file if it exists. @@ -213,19 +222,19 @@ } gentmpconf() { - rm -f "${UPEX4C_outputfile}.tmp" - touch "${UPEX4C_outputfile}.tmp" + rm -f "${UPEX4C_tmp}" + touch "${UPEX4C_tmp}" # this can be removed by the end of 2007 #chown --reference=${TEMPLATEFILE} \ - # ${UPEX4C_outputfile}.tmp ${UPEX4C_outputfile} + # ${UPEX4C_tmp} ${UPEX4C_outputfile} #chmod --reference=${TEMPLATEFILE} \ - # ${UPEX4C_outputfile}.tmp ${UPEX4C_outputfile} + # ${UPEX4C_tmp} ${UPEX4C_outputfile} if [ "$(id -u)" = "0" ]; then - chown root:Debian-exim "${UPEX4C_outputfile}.tmp" + chown root:Debian-exim "${UPEX4C_tmp}" [ -e "${UPEX4C_outputfile}" ] && \ chown root:Debian-exim "${UPEX4C_outputfile}" fi - chmod 640 "${UPEX4C_outputfile}.tmp" + chmod 640 "${UPEX4C_tmp}" if [ -e "${UPEX4C_outputfile}" ]; then chmod 640 "${UPEX4C_outputfile}" fi @@ -241,7 +250,7 @@ gentmpconf -cat << EOF >> "${UPEX4C_outputfile}.tmp" +cat << EOF >> "${UPEX4C_tmp}" ######### # WARNING WARNING WARNING # WARNING WARNING WARNING @@ -252,17 +261,17 @@ EOF if [ "${dc_use_split_config}" = "true" ] ; then -cat << EOF >> "${UPEX4C_outputfile}.tmp" +cat << EOF >> "${UPEX4C_tmp}" # split config files in the $UPEX4C_confd/ directory. EOF else -cat << EOF >> "${UPEX4C_outputfile}.tmp" +cat << EOF >> "${UPEX4C_tmp}" # non-split config ($UPEX4C_confdir/exim4.conf.localmacros # and $UPEX4C_confdir/exim4.conf.template). EOF fi -cat << EOF >> "${UPEX4C_outputfile}.tmp" +cat << EOF >> "${UPEX4C_tmp}" # The config files are supplemented with package installation/configuration # settings managed by debconf. This data is stored in # $UPEX4C_confdir/update-exim4.conf.conf @@ -319,7 +328,7 @@ cat_parts "${UPEX4C_confd}/$i" done | \ removecomments \ - >> "${UPEX4C_outputfile}.tmp" + >> "${UPEX4C_tmp}" else LOCALMACROS="" if [ -e "/etc/exim4/exim4.conf.localmacros" ]; then @@ -327,9 +336,9 @@ fi cat "${LOCALMACROS:-/dev/null}" "${TEMPLATEFILE:-/dev/null}" | \ removecomments \ - >> "${UPEX4C_outputfile}.tmp" + >> "${UPEX4C_tmp}" fi - mv -f "${UPEX4C_outputfile}.tmp" "${UPEX4C_outputfile}" + mv -f "${UPEX4C_tmp}" "${UPEX4C_outputfile}" chmod "${CFILEMODE}" "${UPEX4C_outputfile}" [ "${UPEX4C_verbose}" = "yes" ] && \ echo "Not substituting variables since conftype is none (or other)" @@ -409,7 +418,7 @@ done \ | removecomments \ | sed "s|^\(UPEX4CmacrosUPEX4C.*\)$|\1\n$UPEX4C_macros|" \ - >> "${UPEX4C_outputfile}.tmp" + >> "${UPEX4C_tmp}" RELEVANTTEMPLATE="$UPEX4C_confd" ;; false) @@ -424,12 +433,12 @@ cat "${LOCALMACROS:-/dev/null}" "${TEMPLATEFILE:-/dev/null}" \ | removecomments \ | sed "s|^\(UPEX4CmacrosUPEX4C.*\)$|\1\n$UPEX4C_macros|" \ - >> "${UPEX4C_outputfile}.tmp" + >> "${UPEX4C_tmp}" RELEVANTTEMPLATE="$TEMPLATEFILE" ;; *) errormessage "Invalid value for dc_use_split_config: \"${dc_use_split_config}\", exiting." - rm -f "${UPEX4C_outputfile}.tmp" + rm -f "${UPEX4C_tmp}" exit 1 ;; esac @@ -451,19 +460,25 @@ fi -# test validity if called without -o -if [ "${UPEX4C_outputfile}" = "${UPEX4C_autoconfigfile}" ] && \ - [ -x "${EXIM}" ] ; then - if ! "${EXIM}" -C "${UPEX4C_outputfile}.tmp" -bV > /dev/null ; then - # we have an error in the configuration file. Do not install - # and activate. However, errors in string expansions inside - # the configuration file are not detected by this check! - errormessage "Invalid new configfile ${UPEX4C_outputfile}.tmp, not installing ${UPEX4C_outputfile}.tmp to ${UPEX4C_outputfile}" - exit 1 +# test validity if called without -o or if --check was supplied +if [ "${UPEX4C_outputfile}" = "${UPEX4C_autoconfigfile}" ] || \ + [ "x${UPEX4C_check}" = "xyes" ]; then + if [ -x "${EXIM}" ] ; then + if ! "${EXIM}" -C "${UPEX4C_tmp}" -bV > /dev/null ; then + # we have an error in the configuration file. Do not install + # and activate. However, errors in string expansions inside + # the configuration file are not detected by this check! + errormessage "Invalid new configfile ${UPEX4C_tmp}, not installing ${UPEX4C_tmp} to ${UPEX4C_outputfile}" + exit 1 + fi fi fi +if [ "x${UPEX4C_check}" = "xyes" ]; then + rm -f "${UPEX4C_tmp}" + exit 0 +fi -mv -f "${UPEX4C_outputfile}.tmp" "${UPEX4C_outputfile}" +mv -f "${UPEX4C_tmp}" "${UPEX4C_outputfile}" chmod "${CFILEMODE}" "${UPEX4C_outputfile}" # end of file diff -Nru exim4-4.84/debian/EDITME.exim4-heavy.diff exim4-4.86~RC4/debian/EDITME.exim4-heavy.diff --- exim4-4.84/debian/EDITME.exim4-heavy.diff 2014-07-14 14:08:17.000000000 +0200 +++ exim4-4.86~RC4/debian/EDITME.exim4-heavy.diff 2015-06-23 19:10:26.000000000 +0200 @@ -1,5 +1,5 @@ ---- EDITME.exim4-light 2012-05-18 20:11:24.000000000 +0200 -+++ EDITME.exim4-heavy 2012-05-18 20:13:56.000000000 +0200 +--- EDITME.exim4-light 2015-06-06 14:32:13.155250364 +0200 ++++ EDITME.exim4-heavy 2015-06-06 14:33:37.665534341 +0200 @@ -212,7 +212,7 @@ ROUTER_REDIRECT=yes # This one is very special-purpose, so is not included by default. @@ -46,7 +46,7 @@ # LDAP_LIB_TYPE=NETSCAPE # LDAP_LIB_TYPE=SOLARIS -@@ -366,6 +366,9 @@ LOOKUP_PASSWD=yes +@@ -366,6 +366,9 @@ PCRE_CONFIG=yes # LOOKUP_LIBS=-L/usr/local/lib -lldap -llber -lmysqlclient -lpq -lgds -lsqlite3 @@ -56,7 +56,7 @@ #------------------------------------------------------------------------------ # Compiling the Exim monitor: If you want to compile the Exim monitor, a # program that requires an X11 display, then EXIM_MONITOR should be set to the -@@ -374,7 +377,7 @@ LOOKUP_PASSWD=yes +@@ -374,7 +377,7 @@ PCRE_CONFIG=yes # files are defaulted in the OS/Makefile-Default file, but can be overridden in # local OS-specific make files. @@ -82,7 +82,7 @@ # If you're using ClamAV and are backporting fixes to an old version, instead # of staying current (which is the more usual approach) then you may need to -@@ -578,14 +581,14 @@ WHITELIST_D_MACROS=OUTGOING +@@ -633,15 +636,15 @@ WHITELIST_D_MACROS=OUTGOING # configuration to make use of the mechanism(s) selected. AUTH_CRAM_MD5=yes @@ -96,11 +96,13 @@ # AUTH_HEIMDAL_GSSAPI_PC=heimdal-gssapi AUTH_PLAINTEXT=yes -# AUTH_SPA=yes +-# AUTH_TLS=yes +AUTH_SPA=yes ++AUTH_TLS=yes #------------------------------------------------------------------------------ -@@ -595,7 +598,7 @@ AUTH_PLAINTEXT=yes +@@ -651,7 +654,7 @@ AUTH_PLAINTEXT=yes # Similarly for GNU SASL, unless pkg-config is used via AUTH_GSASL_PC. # Ditto for AUTH_HEIMDAL_GSSAPI(_PC). @@ -109,7 +111,7 @@ # AUTH_LIBS=-lgsasl # AUTH_LIBS=-lgssapi -lheimntlm -lkrb5 -lhx509 -lcom_err -lhcrypto -lasn1 -lwind -lroken -lcrypt -@@ -830,7 +833,7 @@ ZCAT_COMMAND=/bin/zcat +@@ -914,7 +917,7 @@ ZCAT_COMMAND=zcat # (version 5.004 or later) installed, set EXIM_PERL to perl.o. Using embedded # Perl costs quite a lot of resources. Only do this if you really need it. @@ -118,7 +120,7 @@ #------------------------------------------------------------------------------ -@@ -840,7 +843,7 @@ ZCAT_COMMAND=/bin/zcat +@@ -924,7 +927,7 @@ ZCAT_COMMAND=zcat # that the local_scan API is made available by the linker. You may also need # to add -ldl to EXTRALIBS so that dlopen() is available to Exim. @@ -127,7 +129,7 @@ #------------------------------------------------------------------------------ -@@ -850,11 +853,11 @@ ZCAT_COMMAND=/bin/zcat +@@ -934,11 +937,11 @@ ZCAT_COMMAND=zcat # support, which is intended for use in conjunction with the SMTP AUTH # facilities, is included only when requested by the following setting: @@ -141,7 +143,7 @@ #------------------------------------------------------------------------------ -@@ -1174,7 +1177,7 @@ TMPDIR="/tmp" +@@ -1258,7 +1261,7 @@ TMPDIR="/tmp" # local part) can be increased by changing this value. It should be set to # a multiple of 16. diff -Nru exim4-4.84/debian/EDITME.exim4-light.diff exim4-4.86~RC4/debian/EDITME.exim4-light.diff --- exim4-4.84/debian/EDITME.exim4-light.diff 2014-07-14 14:08:17.000000000 +0200 +++ exim4-4.86~RC4/debian/EDITME.exim4-light.diff 2015-06-23 19:10:26.000000000 +0200 @@ -1,5 +1,5 @@ ---- src/EDITME 2012-05-18 19:51:52.000000000 +0200 -+++ EDITME.exim4-light 2012-05-18 19:56:25.000000000 +0200 +--- src/EDITME 2015-06-06 14:26:15.000000000 +0200 ++++ EDITME.exim4-light 2015-06-06 14:28:47.095709200 +0200 @@ -98,7 +98,7 @@ # /usr/local/sbin. The installation script will try to create this directory, # and any superior directories, if they do not exist. @@ -84,7 +84,7 @@ # LOOKUP_PGSQL=yes # LOOKUP_SQLITE=yes # LOOKUP_SQLITE_PC=sqlite3 -@@ -528,7 +529,7 @@ FIXED_NEVER_USERS=root +@@ -583,7 +584,7 @@ FIXED_NEVER_USERS=root # CONFIGURE_OWNER setting, to specify a configuration file which is listed in # the TRUSTED_CONFIG_LIST file, then root privileges are not dropped by Exim. @@ -93,7 +93,7 @@ #------------------------------------------------------------------------------ -@@ -564,6 +565,9 @@ FIXED_NEVER_USERS=root +@@ -619,6 +620,9 @@ FIXED_NEVER_USERS=root # WHITELIST_D_MACROS=TLS:SPOOL @@ -103,7 +103,7 @@ #------------------------------------------------------------------------------ # Exim has support for the AUTH (authentication) extension of the SMTP # protocol, as defined by RFC 2554. If you don't know what SMTP authentication -@@ -573,14 +577,14 @@ FIXED_NEVER_USERS=root +@@ -628,14 +632,14 @@ FIXED_NEVER_USERS=root # included in the Exim binary. You will then need to set up the run time # configuration to make use of the mechanism(s) selected. @@ -118,9 +118,9 @@ -# AUTH_PLAINTEXT=yes +AUTH_PLAINTEXT=yes # AUTH_SPA=yes + # AUTH_TLS=yes - -@@ -602,7 +606,7 @@ FIXED_NEVER_USERS=root +@@ -658,7 +662,7 @@ FIXED_NEVER_USERS=root # one that is set in the headers_charset option. The default setting is # defined by this setting: @@ -129,7 +129,7 @@ # If you are going to make use of $header_xxx expansions in your configuration # file, or if your users are going to use them in filter files, and the normal -@@ -684,7 +688,7 @@ HEADERS_CHARSET="ISO-8859-1" +@@ -740,7 +744,7 @@ HEADERS_CHARSET="ISO-8859-1" # leave these settings commented out. # This setting is required for any TLS support (either OpenSSL or GnuTLS) @@ -138,7 +138,7 @@ # Uncomment one of these settings if you are using OpenSSL; pkg-config vs not # USE_OPENSSL_PC=openssl -@@ -692,9 +696,9 @@ HEADERS_CHARSET="ISO-8859-1" +@@ -748,9 +752,9 @@ HEADERS_CHARSET="ISO-8859-1" # Uncomment the first and either the second or the third of these if you # are using GnuTLS. If you have pkg-config, then the second, else the third. @@ -148,9 +148,9 @@ -# TLS_LIBS=-lgnutls -ltasn1 -lgcrypt +TLS_LIBS=-lgnutls - # If you are running Exim as a server, note that just building it with TLS - # support is not all you need to do. You also need to set up a suitable -@@ -775,6 +779,7 @@ CFLAGS += -fvisibility=hidden + # The security fix we provide with the gnutls_allow_auto_pkcs11 option + # (4.82 PP/09) introduces a compatibility regression. The symbol is +@@ -838,6 +842,7 @@ CFLAGS += -fvisibility=hidden # to form the final file names. Some installations may want something like this: # LOG_FILE_PATH=/var/log/exim_%slog @@ -158,7 +158,7 @@ # which results in files with names /var/log/exim_mainlog, etc. The directory # in which the log files are placed must exist; Exim does not try to create -@@ -823,7 +828,7 @@ EXICYCLOG_MAX=10 +@@ -886,7 +891,7 @@ EXICYCLOG_MAX=10 # files. Both the name of the command and the suffix that it adds to files # need to be defined here. See also the EXICYCLOG_MAX configuration. @@ -167,16 +167,16 @@ COMPRESS_SUFFIX=gz -@@ -831,7 +836,7 @@ COMPRESS_SUFFIX=gz - # If the exigrep utility is fed compressed log files, it tries to uncompress - # them using this command. - +@@ -901,7 +906,7 @@ COMPRESS_SUFFIX=gz + # ZCAT_COMMAND=zcat + # + # Or specify the full pathname: -ZCAT_COMMAND=/usr/bin/zcat -+ZCAT_COMMAND=/bin/zcat - ++ZCAT_COMMAND=zcat #------------------------------------------------------------------------------ -@@ -864,6 +869,7 @@ ZCAT_COMMAND=/usr/bin/zcat + # Compiling in support for embedded Perl: If you want to be able to +@@ -933,6 +938,7 @@ ZCAT_COMMAND=/usr/bin/zcat # You probably need to add -lpam to EXTRALIBS, and in some releases of # GNU/Linux -ldl is also needed. @@ -184,7 +184,7 @@ #------------------------------------------------------------------------------ -@@ -930,6 +936,8 @@ ZCAT_COMMAND=/usr/bin/zcat +@@ -999,6 +1005,8 @@ ZCAT_COMMAND=/usr/bin/zcat # CYRUS_SASLAUTHD_SOCKET=/var/state/saslauthd/mux @@ -193,7 +193,7 @@ #------------------------------------------------------------------------------ # TCP wrappers: If you want to use tcpwrappers from within Exim, uncomment -@@ -1233,6 +1241,7 @@ TMPDIR="/tmp" +@@ -1302,6 +1310,7 @@ TMPDIR="/tmp" # file can be specified here. Some installations may want something like this: # PID_FILE_PATH=/var/lock/exim.pid @@ -201,7 +201,7 @@ # If PID_FILE_PATH is not defined, Exim writes a file in its spool directory # using the name "exim-daemon.pid". -@@ -1266,6 +1275,7 @@ TMPDIR="/tmp" +@@ -1335,6 +1344,7 @@ TMPDIR="/tmp" # messages become "invisible" to the normal management tools. # SUPPORT_MOVE_FROZEN_MESSAGES=yes @@ -209,7 +209,7 @@ #------------------------------------------------------------------------------ -@@ -1304,3 +1314,6 @@ TMPDIR="/tmp" +@@ -1373,3 +1383,6 @@ TMPDIR="/tmp" # ENABLE_DISABLE_FSYNC=yes # End of EDITME for Exim 4. diff -Nru exim4-4.84/debian/example.conf.md5 exim4-4.86~RC4/debian/example.conf.md5 --- exim4-4.84/debian/example.conf.md5 2014-07-22 19:16:03.000000000 +0200 +++ exim4-4.86~RC4/debian/example.conf.md5 2015-06-23 19:10:26.000000000 +0200 @@ -1 +1 @@ -c181c27925094f50dbb2f1388602cf03 - +1c61ed190b4dfe5f50ad8f8bf5d99748 - diff -Nru exim4-4.84/debian/exim4-base.exim4.init exim4-4.86~RC4/debian/exim4-base.exim4.init --- exim4-4.84/debian/exim4-base.exim4.init 2014-12-21 14:06:22.000000000 +0100 +++ exim4-4.86~RC4/debian/exim4-base.exim4.init 2015-05-18 20:42:37.000000000 +0200 @@ -48,7 +48,7 @@ for p in $PATH; do if [ -x "$p/$UPEX4CONF" ]; then IFS="$OLDIFS" - $p/$UPEX4CONF $UPEX4OPTS + $p/$UPEX4CONF $UPEX4OPTS $1 return 0 fi done @@ -238,11 +238,13 @@ warn_paniclog ;; restart) + # check whether newly generated config would work + upex4conf --check log_daemon_msg "Stopping MTA for restart" + stop_exim # regenerate exim4.conf upex4conf isconfigvalid - stop_exim log_end_msg 0 sleep 2 log_daemon_msg "Restarting MTA" diff -Nru exim4-4.84/debian/manpages/exim4-config_files.5 exim4-4.86~RC4/debian/manpages/exim4-config_files.5 --- exim4-4.84/debian/manpages/exim4-config_files.5 2014-07-22 19:16:03.000000000 +0200 +++ exim4-4.86~RC4/debian/manpages/exim4-config_files.5 2015-05-18 20:42:37.000000000 +0200 @@ -2,7 +2,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.TH EXIM4-CONFIG_FILES 5 "Jan 5, 2014" EXIM4 +.TH EXIM4-CONFIG_FILES 5 "Jan 4, 2015" EXIM4 .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -291,7 +291,7 @@ .br # the regular expression .br -^smtp[0\-9]*.mail.server.example:user:password +^smtp[0\-9]*\\.mail\\.server\\.example:user:password .br .SH /etc/exim4/exim.crt diff -Nru exim4-4.84/debian/manpages/update-exim4.conf.8 exim4-4.86~RC4/debian/manpages/update-exim4.conf.8 --- exim4-4.84/debian/manpages/update-exim4.conf.8 2014-07-14 14:08:17.000000000 +0200 +++ exim4-4.86~RC4/debian/manpages/update-exim4.conf.8 2015-05-18 20:42:37.000000000 +0200 @@ -25,8 +25,14 @@ .SH OPTIONS .TP -.I \-v|\-\-verbose -Enable verbose mode +.I \-\-check +Generate temporary configuration file, check its validity and exit with +either success (exitcode 0) or an error (exitcode 1). On success the +temorary file is deleted, otherwise the file is left for further +debugging. +.TP +.I \-d|\-\-confdir directory +Read input from directory instead of /etc/exim4. .TP .I \-h|\-\-help Show short help message and exit @@ -34,14 +40,14 @@ .I \-\-keepcomments Do not remove comment lines from the output file. .TP -.I \-\-removecomments -Remove comment lines from the output file. [Default] -.TP .I \-o|\-\-output file Write output to file instead of /var/lib/exim4/config.autogenerated. .TP -.I \-d|\-\-confdir directory -Read input from directory instead of /etc/exim4. +.I \-\-removecomments +Remove comment lines from the output file. [Default] +.TP +.I \-v|\-\-verbose +Enable verbose mode .SH DESCRIPTION The script @@ -335,6 +341,6 @@ /usr/share/doc/exim4\-base/README.Debian.gz .SH AUTHOR -Andreas Metzler +Andreas Metzler .br Marc Haber diff -Nru exim4-4.84/debian/patches/31_eximmanpage.dpatch exim4-4.86~RC4/debian/patches/31_eximmanpage.dpatch --- exim4-4.84/debian/patches/31_eximmanpage.dpatch 2014-12-01 18:45:56.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/31_eximmanpage.dpatch 2015-06-29 07:44:52.000000000 +0200 @@ -161,7 +161,7 @@ .sp In addition, the use of \fB\-f\fP is not restricted when testing a filter file with \fB\-bf\fP or when testing or verifying addresses using the \fB\-bt\fP or -@@ -1271,12 +1271,12 @@ other circumstances, they are ignored un +@@ -1276,12 +1276,12 @@ other circumstances, they are ignored un The \fB\-oMa\fP option sets the sender host address. This may include a port number at the end, after a full stop (period). For example: .sp @@ -176,7 +176,7 @@ .sp The IP address is placed in the \fI$sender_host_address\fP variable, and the port, if present, in \fI$sender_host_port\fP. If both \fB\-oMa\fP and \fB\-bh\fP -@@ -1474,13 +1474,13 @@ When scanning the queue, Exim can be mad +@@ -1479,13 +1479,13 @@ When scanning the queue, Exim can be mad lexically less than a given value by following the \fB\-q\fP option with a starting message id. For example: .sp @@ -192,7 +192,7 @@ .sp just one delivery process is started, for that message. This differs from \fB\-M\fP in that retry data is respected, and it also differs from \fB\-Mc\fP in -@@ -1496,7 +1496,7 @@ starting a queue runner process at inter +@@ -1501,7 +1501,7 @@ starting a queue runner process at inter single daemon process handles both functions. A common way of starting up a combined daemon at system boot time is to use a command such as .sp @@ -201,7 +201,7 @@ .sp Such a daemon listens for incoming SMTP calls, and also starts a queue runner process every 30 minutes. -@@ -1527,7 +1527,7 @@ regular expression; otherwise it is a li +@@ -1532,7 +1532,7 @@ regular expression; otherwise it is a li If you want to do periodic queue runs for messages with specific recipients, you can combine \fB\-R\fP with \fB\-q\fP and a time value. For example: .sp @@ -210,8 +210,8 @@ .sp This example does a queue run for messages with recipients in the given domain every 25 minutes. Any additional flags that are specified with \fB\-q\fP are -@@ -1637,6 +1637,26 @@ to the named file. It is ignored by Exi - .sp +@@ -1647,6 +1647,26 @@ Quotes should be used to maintain a mult + under most shells. . .SH "SEE ALSO" +.BR exicyclog (8), diff -Nru exim4-4.84/debian/patches/32_exim4.dpatch exim4-4.86~RC4/debian/patches/32_exim4.dpatch --- exim4-4.84/debian/patches/32_exim4.dpatch 2014-07-22 19:16:03.000000000 +0200 +++ exim4-4.86~RC4/debian/patches/32_exim4.dpatch 2015-06-23 19:10:26.000000000 +0200 @@ -4,8 +4,8 @@ Forwarded: not-needed Last-Update: 2013-09-28 ---- exim4-4.82~rc1.orig/OS/Makefile-Linux -+++ exim4-4.82~rc1/OS/Makefile-Linux +--- a/OS/Makefile-Linux ++++ b/OS/Makefile-Linux @@ -28,9 +28,9 @@ XLFLAGS=-L$(X11)/lib X11_LD_LIB=$(X11)/lib @@ -18,8 +18,8 @@ EXIWHAT_KILL_SIGNAL=-USR1 # End ---- exim4-4.82~rc1.orig/src/exicyclog.src -+++ exim4-4.82~rc1/src/exicyclog.src +--- a/src/exicyclog.src ++++ b/src/exicyclog.src @@ -144,7 +144,7 @@ done st=' ' @@ -29,8 +29,8 @@ spool_directory=`$exim_path -C $config -bP spool_directory | sed 's/.*=[ ]*//'` ---- exim4-4.82~rc1.orig/src/exim_checkaccess.src -+++ exim4-4.82~rc1/src/exim_checkaccess.src +--- a/src/exim_checkaccess.src ++++ b/src/exim_checkaccess.src @@ -52,7 +52,7 @@ done # a tab to keep the tab in one place. @@ -40,8 +40,8 @@ ######################################################################### ---- exim4-4.82~rc1.orig/src/eximon.src -+++ exim4-4.82~rc1/src/eximon.src +--- a/src/eximon.src ++++ b/src/eximon.src @@ -72,7 +72,7 @@ config=${EXIMON_EXIM_CONFIG-$config} st=' ' @@ -51,8 +51,8 @@ SPOOL_DIRECTORY=`$EXIM_PATH -C $config -bP spool_directory | sed 's/.*=[ ]*//'` LOG_FILE_PATH=`$EXIM_PATH -C $config -bP log_file_path | sed 's/.*=[ ]*//'` ---- exim4-4.82~rc1.orig/src/exinext.src -+++ exim4-4.82~rc1/src/exinext.src +--- a/src/exinext.src ++++ b/src/exinext.src @@ -90,7 +90,7 @@ if [ "$exim_path" = "" ]; then exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` fi @@ -71,8 +71,8 @@ die "can't run exim_dumpdb"; while () ---- exim4-4.82~rc1.orig/src/exiqgrep.src -+++ exim4-4.82~rc1/src/exiqgrep.src +--- a/src/exiqgrep.src ++++ b/src/exiqgrep.src @@ -21,7 +21,7 @@ use strict; use Getopt::Std; @@ -82,8 +82,8 @@ my $eargs = '-bpu'; my %id; my %opt; ---- exim4-4.82~rc1.orig/src/exiwhat.src -+++ exim4-4.82~rc1/src/exiwhat.src +--- a/src/exiwhat.src ++++ b/src/exiwhat.src @@ -88,7 +88,7 @@ fi st=' ' @@ -93,9 +93,9 @@ spool_directory=`$exim_path -C $config -bP spool_directory | sed "s/.*=[ ]*//"` process_log_path=`$exim_path -C $config -bP process_log_path | sed "s/.*=[ ]*//"` ---- exim4-4.82~rc1.orig/src/globals.c -+++ exim4-4.82~rc1/src/globals.c -@@ -633,7 +633,7 @@ int errors_sender_rc = EXIT_FA +--- a/src/globals.c ++++ b/src/globals.c +@@ -685,7 +685,7 @@ const uschar *event_name = NULL; gid_t exim_gid = EXIM_GID; BOOL exim_gid_set = TRUE; /* This gid is always set */ diff -Nru exim4-4.84/debian/patches/40_reproducible_build.diff exim4-4.86~RC4/debian/patches/40_reproducible_build.diff --- exim4-4.84/debian/patches/40_reproducible_build.diff 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/40_reproducible_build.diff 2015-06-23 19:10:26.000000000 +0200 @@ -0,0 +1,74 @@ +Description: +Author: Andreas Metzler + +--- +The information above should follow the Patch Tagging Guidelines, please +checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here +are templates for supplementary fields that you might want to add: + +Origin: , +Bug: +Bug-Debian: https://bugs.debian.org/ +Bug-Ubuntu: https://launchpad.net/bugs/ +Forwarded: +Reviewed-By: +Last-Update: + +--- a/exim_monitor/em_version.c ++++ b/exim_monitor/em_version.c +@@ -10,6 +10,8 @@ + #include + #include + ++#include "../src/repbuildtime.h" ++ + extern uschar *version_string; + extern uschar *version_date; + +@@ -21,7 +23,7 @@ uschar today[20]; + + version_string = US"2.06"; + +-Ustrcpy(today, __DATE__); ++Ustrcpy(today, REPBUILDDATE); + if (today[4] == ' ') i = 1; + today[3] = today[6] = '-'; + +@@ -31,7 +33,7 @@ Ustrncat(version_date, today+4+i, 3-i); + Ustrncat(version_date, today, 4); + Ustrncat(version_date, today+7, 4); + Ustrcat(version_date, " "); +-Ustrcat(version_date, __TIME__); ++Ustrcat(version_date, REPBUILDTIME); + } + + /* End of em_version.c */ +--- a/src/version.c ++++ b/src/version.c +@@ -11,6 +11,8 @@ + + #include "version.h" + ++#include "../src/repbuildtime.h" ++ + + /* The header file cnumber.h contains a single line containing the + compilation number, making it easy to have it updated automatically. +@@ -40,7 +42,7 @@ version_cnumber_format = US"%d\0<>"; + +-Ustrcpy(today, __DATE__); ++Ustrcpy(today, REPBUILDDATE); + if (today[4] == ' ') today[4] = '0'; + today[3] = today[6] = '-'; + +@@ -50,7 +52,7 @@ Ustrncat(version_date, today+4, 3); + Ustrncat(version_date, today, 4); + Ustrncat(version_date, today+7, 4); + Ustrcat(version_date, " "); +-Ustrcat(version_date, __TIME__); ++Ustrcat(version_date, REPBUILDTIME); + } + + /* End of version.c */ diff -Nru exim4-4.84/debian/patches/50_localscan_dlopen.dpatch exim4-4.86~RC4/debian/patches/50_localscan_dlopen.dpatch --- exim4-4.84/debian/patches/50_localscan_dlopen.dpatch 2014-12-21 14:05:01.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/50_localscan_dlopen.dpatch 2015-06-23 19:10:26.000000000 +0200 @@ -9,11 +9,11 @@ Author: David Woodhouse, Derrick 'dman' Hudson, Marc MERLIN Origin: other, http://marc.merlins.org/linux/exim/files/sa-exim-current/ Forwarded: no -Last-Update: 2013-09-28 +Last-Update: 2014-12-01 --- a/src/EDITME +++ b/src/EDITME -@@ -783,6 +783,21 @@ HEADERS_CHARSET="ISO-8859-1" +@@ -794,6 +794,21 @@ HEADERS_CHARSET="ISO-8859-1" #------------------------------------------------------------------------------ @@ -37,7 +37,7 @@ # the documentation in "info" format, first fetch the Texinfo documentation --- a/src/config.h.defaults +++ b/src/config.h.defaults -@@ -27,6 +27,8 @@ it's a default value. */ +@@ -28,6 +28,8 @@ it's a default value. */ #define AUTH_VARS 3 @@ -48,9 +48,9 @@ #define CONFIGURE_FILE --- a/src/globals.c +++ b/src/globals.c -@@ -134,6 +134,10 @@ BOOL smtp_use_dsn = FALSE; +@@ -140,6 +140,10 @@ const pcre *regex_DSN = NULL; + BOOL smtp_use_dsn = FALSE; uschar *dsn_advertise_hosts = NULL; - #endif +#ifdef DLOPEN_LOCAL_SCAN +uschar *local_scan_path = NULL; @@ -61,9 +61,9 @@ BOOL gnutls_allow_auto_pkcs11 = FALSE; --- a/src/globals.h +++ b/src/globals.h -@@ -134,6 +134,9 @@ extern BOOL smtp_use_dsn; / +@@ -136,6 +136,9 @@ extern const pcre *regex_DSN; / + extern BOOL smtp_use_dsn; /* Global for passed connections */ extern uschar *dsn_advertise_hosts; /* host for which TLS is advertised */ - #endif +#ifdef DLOPEN_LOCAL_SCAN +extern uschar *local_scan_path; /* Path to local_scan() library */ @@ -262,8 +262,8 @@ #include "config.h" #include "mytypes.h" #include "store.h" -@@ -194,4 +195,6 @@ extern uschar *string_copy(const uschar - extern uschar *string_copyn(uschar *, int); +@@ -192,4 +193,6 @@ extern uschar *string_copy(const uschar + extern uschar *string_copyn(const uschar *, int); extern uschar *string_sprintf(const char *, ...) ALMOST_PRINTF(1,2); +#pragma GCC visibility pop @@ -271,7 +271,7 @@ /* End of local_scan.h */ --- a/src/readconf.c +++ b/src/readconf.c -@@ -289,6 +289,9 @@ static optionlist optionlist_config[] = +@@ -294,6 +294,9 @@ static optionlist optionlist_config[] = { "local_from_prefix", opt_stringptr, &local_from_prefix }, { "local_from_suffix", opt_stringptr, &local_from_suffix }, { "local_interfaces", opt_stringptr, &local_interfaces }, diff -Nru exim4-4.84/debian/patches/66_enlarge-dh-parameters-size.dpatch exim4-4.86~RC4/debian/patches/66_enlarge-dh-parameters-size.dpatch --- exim4-4.84/debian/patches/66_enlarge-dh-parameters-size.dpatch 2014-12-21 14:05:01.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/66_enlarge-dh-parameters-size.dpatch 2015-06-23 19:10:26.000000000 +0200 @@ -5,16 +5,16 @@ Author: Marc Haber Origin: vendor Forwarded: no -Last-Update: 2013-09-28 +Last-Update: 2014-11-18 ---- exim4-4.82~rc1.orig/src/tls-gnu.c -+++ exim4-4.82~rc1/src/tls-gnu.c -@@ -164,7 +164,7 @@ callbacks. */ +--- a/src/tls-gnu.c ++++ b/src/tls-gnu.c +@@ -197,7 +197,7 @@ callbacks. */ can ask for a bit-strength. Without that, we stick to the constant we had before, for now. */ #ifndef EXIM_SERVER_DH_BITS_PRE2_12 --#define EXIM_SERVER_DH_BITS_PRE2_12 1024 -+#define EXIM_SERVER_DH_BITS_PRE2_12 2048 +-# define EXIM_SERVER_DH_BITS_PRE2_12 1024 ++# define EXIM_SERVER_DH_BITS_PRE2_12 2048 #endif #define exim_gnutls_err_check(Label) do { \ diff -Nru exim4-4.84/debian/patches/67_unnecessaryCopt.diff exim4-4.86~RC4/debian/patches/67_unnecessaryCopt.diff --- exim4-4.84/debian/patches/67_unnecessaryCopt.diff 2014-12-21 14:05:01.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/67_unnecessaryCopt.diff 2015-05-18 20:42:37.000000000 +0200 @@ -2,11 +2,11 @@ et al.) since this breaks with ALT_CONFIG_PREFIX. Author: Andreas Metzler Forwarded: http://bugs.exim.org/show_bug.cgi?id=1045 -Last-Update: 2010-12-12 +Last-Update: 2014-12-01 ---- exim4-4.72.orig/src/exicyclog.src -+++ exim4-4.72/src/exicyclog.src -@@ -147,10 +147,10 @@ st=' ' +--- a/src/exicyclog.src ++++ b/src/exicyclog.src +@@ -146,10 +146,10 @@ st=' ' exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi @@ -19,9 +19,9 @@ fi # If log_file_path contains only "syslog" then no Exim log files are in use. ---- exim4-4.72.orig/src/eximon.src -+++ exim4-4.72/src/eximon.src -@@ -68,8 +68,8 @@ st=' ' +--- a/src/eximon.src ++++ b/src/eximon.src +@@ -74,8 +74,8 @@ st=' ' EXIM_PATH=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` if test "$EXIM_PATH" = ""; then EXIM_PATH=BIN_DIRECTORY/exim4; fi @@ -32,9 +32,9 @@ # If log_file_path is "syslog" then logging is only to syslog, and the monitor # is unable to display a log tail unless EXIMON_LOG_FILE_PATH is set to tell ---- exim4-4.72.orig/src/exinext.src -+++ exim4-4.72/src/exinext.src -@@ -92,8 +92,8 @@ if [ "$exim_path" = "" ]; then +--- a/src/exinext.src ++++ b/src/exinext.src +@@ -91,8 +91,8 @@ if [ "$exim_path" = "" ]; then fi if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi @@ -45,7 +45,7 @@ # Now do the job. Perl uses $ so frequently that we don't want to have to # escape them all from the shell, so pass in shell variable values as -@@ -135,7 +135,7 @@ perl - $exim_path "$eximmacdef" $argone +@@ -134,7 +134,7 @@ perl - $exim_path "$eximmacdef" $argone # Run Exim to get a list of hosts for the given domain; for # each one construct the appropriate retry key. @@ -54,9 +54,9 @@ die "can't run exim to route $address"; while () ---- exim4-4.72.orig/src/exiwhat.src -+++ exim4-4.72/src/exiwhat.src -@@ -90,8 +90,8 @@ fi +--- a/src/exiwhat.src ++++ b/src/exiwhat.src +@@ -89,8 +89,8 @@ fi st=' ' exim_path=`grep "^[$st]*exim_path" $config | sed "s/.*=[$st]*//"` if test "$exim_path" = ""; then exim_path=BIN_DIRECTORY/exim4; fi diff -Nru exim4-4.84/debian/patches/70_remove_exim-users_references.dpatch exim4-4.86~RC4/debian/patches/70_remove_exim-users_references.dpatch --- exim4-4.84/debian/patches/70_remove_exim-users_references.dpatch 2014-12-21 14:05:01.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/70_remove_exim-users_references.dpatch 2015-05-18 20:42:37.000000000 +0200 @@ -3,11 +3,11 @@ ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: No description. +Last-Update: 2014-12-01 -diff -NurbBp exim.orig/README exim/README ---- exim.orig/README 2005-08-30 12:07:58.000000000 +0200 -+++ exim/README 2009-11-15 12:17:48.000000000 +0100 -@@ -16,8 +16,16 @@ from Exim 3, though the basic structure +--- a/README ++++ b/README +@@ -14,8 +14,16 @@ from Exim 3, though the basic structure older book may be helpful for the background, but a lot of the detail has changed, so it is likely to be confusing to newcomers. @@ -26,10 +26,9 @@ A copy of the Exim FAQ should be available from the same source that you used to obtain the Exim distribution. Additional formats for the documentation -diff -NurbBp exim.orig/src/eximstats.src exim/src/eximstats.src ---- exim.orig/src/eximstats.src 2009-11-15 12:16:19.000000000 +0100 -+++ exim/src/eximstats.src 2009-11-15 12:17:48.000000000 +0100 -@@ -536,8 +536,7 @@ about how to create charts from the tabl +--- a/src/eximstats.src ++++ b/src/eximstats.src +@@ -537,8 +537,7 @@ about how to create charts from the tabl =head1 AUTHOR diff -Nru exim4-4.84/debian/patches/80_mime_empty_charset.diff exim4-4.86~RC4/debian/patches/80_mime_empty_charset.diff --- exim4-4.84/debian/patches/80_mime_empty_charset.diff 2014-12-21 14:05:01.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/80_mime_empty_charset.diff 1970-01-01 01:00:00.000000000 +0100 @@ -1,60 +0,0 @@ -From 93cad488cb2c9a31aea345c8910a9f9c5815071c Mon Sep 17 00:00:00 2001 -From: Jeremy Harris -Date: Fri, 29 Aug 2014 14:11:50 +0100 -Subject: [PATCH] Fix crash in mime acl when a parameter is zero-length - - -diff --git a/src/mime.c b/src/mime.c -index 95d3da4..ab701f2 100644 ---- a/src/mime.c -+++ b/src/mime.c -@@ -620,12 +620,18 @@ NEXT_PARAM_SEARCH: - else - param_value = string_cat(param_value, &size, &ptr, q++, 1); - } -- param_value[ptr++] = '\0'; -- param_value_len = ptr; -- -- param_value = rfc2047_decode(param_value, check_rfc2047_length, NULL, 32, ¶m_value_len, &q); -- debug_printf("Found %s MIME parameter in %s header, value is '%s'\n", mp->name, mime_header_list[i].name, param_value); -- *((uschar **)(mp->value)) = param_value; -+ if (param_value) -+ { -+ param_value[ptr++] = '\0'; -+ param_value_len = ptr; -+ -+ param_value = rfc2047_decode(param_value, -+ check_rfc2047_length, NULL, 32, ¶m_value_len, &q); -+ debug_printf("Found %s MIME parameter in %s header, " -+ "value is '%s'\n", mp->name, mime_header_list[i].name, -+ param_value); -+ } -+ *mp->value = param_value; - p += (mp->namelen + param_value_len + 1); - goto NEXT_PARAM_SEARCH; - } -diff --git a/src/mime.h b/src/mime.h -index abf68da..af09f67 100644 ---- a/src/mime.h -+++ b/src/mime.h -@@ -40,15 +40,15 @@ static int mime_header_list_size = sizeof(mime_header_list)/sizeof(mime_header); - - - typedef struct mime_parameter { -- uschar *name; -- int namelen; -- void *value; -+ uschar * name; -+ int namelen; -+ uschar ** value; - } mime_parameter; - - static mime_parameter mime_parameter_list[] = { -- { US"name=", 5, &mime_filename }, -+ { US"name=", 5, &mime_filename }, - { US"filename=", 9, &mime_filename }, -- { US"charset=", 8, &mime_charset }, -+ { US"charset=", 8, &mime_charset }, - { US"boundary=", 9, &mime_boundary } - }; - diff -Nru exim4-4.84/debian/patches/81_buffer-overrun-in-spam-acl.diff exim4-4.86~RC4/debian/patches/81_buffer-overrun-in-spam-acl.diff --- exim4-4.84/debian/patches/81_buffer-overrun-in-spam-acl.diff 2014-12-21 14:05:01.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/81_buffer-overrun-in-spam-acl.diff 1970-01-01 01:00:00.000000000 +0100 @@ -1,26 +0,0 @@ -From e252eb8c71ea3bddb32bf73bddc8b22cfde2bc3a Mon Sep 17 00:00:00 2001 -From: Jeremy Harris -Date: Thu, 27 Nov 2014 16:26:44 +0000 -Subject: [PATCH] Fix buffer overrun in spam= acl condition. Bug 1552 - ---- - src/spam.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/spam.c b/src/spam.c -index 7eb6fbf..76bf7d6 100644 ---- a/src/spam.c -+++ b/src/spam.c -@@ -129,7 +129,8 @@ spam(uschar **listptr) - (spamd_address_container *)store_get(sizeof(spamd_address_container)); - - /* grok spamd address and port */ -- if( sscanf(CS address, "%s %u", this_spamd->tcp_addr, &(this_spamd->tcp_port)) != 2 ) { -+ if (sscanf(CS address, "%23s %u", this_spamd->tcp_addr, &(this_spamd->tcp_port)) != 2) -+ { - log_write(0, LOG_MAIN, - "spam acl condition: warning - invalid spamd address: '%s'", address); - continue; --- -2.1.3 - diff -Nru exim4-4.84/debian/patches/82_quoted-or-r-2047-encoded.diff exim4-4.86~RC4/debian/patches/82_quoted-or-r-2047-encoded.diff --- exim4-4.84/debian/patches/82_quoted-or-r-2047-encoded.diff 2014-12-21 14:05:01.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/82_quoted-or-r-2047-encoded.diff 1970-01-01 01:00:00.000000000 +0100 @@ -1,194 +0,0 @@ -From 5c6cf6a0d5cb7da39e7fde01dca1ff862c1fa1c8 Mon Sep 17 00:00:00 2001 -From: Jeremy Harris -Date: Sun, 14 Dec 2014 15:15:34 +0000 -Subject: [PATCH] Account properly for quoted or 2047-encoded MIME parameters - while walking headers. Bug 1558 - ---- - src/mime.c | 103 ++++++++++++++++++++++------------------ - test/log/4000 | 3 ++ - test/mail/4000.userx | 38 +++++++++++++++ - test/scripts/4000-scanning/4000 | 29 +++++++++++ - test/stdout/4000 | 11 +++++ - 5 files changed, 137 insertions(+), 47 deletions(-) - -diff --git a/src/mime.c b/src/mime.c -index ab701f2..a61e9f2 100644 ---- a/src/mime.c -+++ b/src/mime.c -@@ -528,26 +528,24 @@ while(1) - */ - if (context != NULL) - { -- while(fgets(CS header, MIME_MAX_HEADER_SIZE, f) != NULL) -+ while(fgets(CS header, MIME_MAX_HEADER_SIZE, f)) - { - /* boundary line must start with 2 dashes */ -- if (Ustrncmp(header,"--",2) == 0) -- { -- if (Ustrncmp((header+2),context->boundary,Ustrlen(context->boundary)) == 0) -+ if ( Ustrncmp(header, "--", 2) == 0 -+ && Ustrncmp(header+2, context->boundary, Ustrlen(context->boundary)) == 0) -+ { -+ /* found boundary */ -+ if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0) - { -- /* found boundary */ -- if (Ustrncmp((header+2+Ustrlen(context->boundary)),"--",2) == 0) -- { -- /* END boundary found */ -- debug_printf("End boundary found %s\n", context->boundary); -- return rc; -- } -- else -- debug_printf("Next part with boundary %s\n", context->boundary); -- -- /* can't use break here */ -- goto DECODE_HEADERS; -+ /* END boundary found */ -+ debug_printf("End boundary found %s\n", context->boundary); -+ return rc; - } -+ else -+ debug_printf("Next part with boundary %s\n", context->boundary); -+ -+ /* can't use break here */ -+ goto DECODE_HEADERS; - } - } - /* Hit EOF or read error. Ugh. */ -@@ -557,92 +555,103 @@ while(1) - - DECODE_HEADERS: - /* parse headers, set up expansion variables */ -- while (mime_get_header(f,header)) -+ while (mime_get_header(f, header)) - { - int i; - /* loop through header list */ - for (i = 0; i < mime_header_list_size; i++) -- { -- uschar *header_value = NULL; -- int header_value_len = 0; -- -- /* found an interesting header? */ -- if (strncmpic(mime_header_list[i].name,header,mime_header_list[i].namelen) == 0) -- { -- uschar *p = header + mime_header_list[i].namelen; -- /* yes, grab the value (normalize to lower case) -- and copy to its corresponding expansion variable */ -+ if (strncmpic(mime_header_list[i].name, -+ header, mime_header_list[i].namelen) == 0) -+ { /* found an interesting header */ -+ uschar * header_value; -+ int header_value_len; -+ uschar * p = header + mime_header_list[i].namelen; -+ -+ /* grab the value (normalize to lower case) -+ and copy to its corresponding expansion variable */ - while(*p != ';') - { - *p = tolower(*p); - p++; - } -- header_value_len = (p - (header + mime_header_list[i].namelen)); -- header_value = (uschar *)malloc(header_value_len+1); -- memset(header_value,0,header_value_len+1); -+ header_value_len = p - (header + mime_header_list[i].namelen); - p = header + mime_header_list[i].namelen; -- Ustrncpy(header_value, p, header_value_len); -- debug_printf("Found %s MIME header, value is '%s'\n", mime_header_list[i].name, header_value); -+ header_value = string_copyn(p, header_value_len); -+ debug_printf("Found %s MIME header, value is '%s'\n", -+ mime_header_list[i].name, header_value); - *((uschar **)(mime_header_list[i].value)) = header_value; - - /* make p point to the next character after the closing ';' */ -- p += (header_value_len+1); -+ p += header_value_len+1; - -- /* grab all param=value tags on the remaining line, check if they are interesting */ -+ /* grab all param=value tags on the remaining line, -+ check if they are interesting */ - NEXT_PARAM_SEARCH: -- while (*p != 0) -+ while (*p) - { - mime_parameter * mp; - for (mp = mime_parameter_list; - mp < &mime_parameter_list[mime_parameter_list_size]; - mp++) - { -- uschar *param_value = NULL; -- int param_value_len = 0; -+ uschar * param_value = NULL; - - /* found an interesting parameter? */ - if (strncmpic(mp->name, p, mp->namelen) == 0) - { -- uschar *q = p + mp->namelen; -+ uschar * q = p + mp->namelen; -+ int plen = 0; - int size = 0; - int ptr = 0; - - /* yes, grab the value and copy to its corresponding expansion variable */ - while(*q && *q != ';') /* ; terminates */ -- { - if (*q == '"') - { - q++; /* skip leading " */ -- while(*q && *q != '"') /* which protects ; */ -+ plen++; /* and account for the skip */ -+ while(*q && *q != '"') /* " protects ; */ -+ { - param_value = string_cat(param_value, &size, &ptr, q++, 1); -- if (*q) q++; /* skip trailing " */ -+ plen++; -+ } -+ if (*q) -+ { -+ q++; /* skip trailing " */ -+ plen++; -+ } - } - else -+ { - param_value = string_cat(param_value, &size, &ptr, q++, 1); -- } -+ plen++; -+ } -+ - if (param_value) - { - param_value[ptr++] = '\0'; -- param_value_len = ptr; - - param_value = rfc2047_decode(param_value, -- check_rfc2047_length, NULL, 32, ¶m_value_len, &q); -+ check_rfc2047_length, NULL, 32, NULL, &q); - debug_printf("Found %s MIME parameter in %s header, " - "value is '%s'\n", mp->name, mime_header_list[i].name, - param_value); - } - *mp->value = param_value; -- p += (mp->namelen + param_value_len + 1); -+ p += mp->namelen + plen + 1; /* name=, content, ; */ - goto NEXT_PARAM_SEARCH; - } - } - /* There is something, but not one of our interesting parameters. - Advance to the next semicolon */ -- while(*p != ';') p++; -+ while(*p != ';') -+ { -+ if (*p == '"') while(*++p && *p != '"') ; -+ p++; -+ } - p++; - } - } -- } - } - - /* set additional flag variables (easier access) */ diff -Nru exim4-4.84/debian/patches/83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch exim4-4.86~RC4/debian/patches/83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch --- exim4-4.84/debian/patches/83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch 2015-04-03 14:16:43.000000000 +0200 +++ exim4-4.86~RC4/debian/patches/83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,43 +0,0 @@ -From 8bc732e8b03ebb4309f4b42626917148d176db49 Mon Sep 17 00:00:00 2001 -From: Jeremy Harris -Date: Sun, 5 Oct 2014 21:31:20 +0100 -Subject: [PATCH] Remove limit on remove_headers item size. Bug 1533 - ---- - doc/doc-txt/ChangeLog | 5 +++++ - src/src/transport.c | 3 +-- - 2 files changed, 6 insertions(+), 2 deletions(-) - -| diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog -| index 76ecc20..0b03894 100644 -| --- a/doc/doc-txt/ChangeLog -| +++ b/doc/doc-txt/ChangeLog -| @@ -44,6 +44,11 @@ JH/05 Fix results-pipe from transport process. Several recipients, combined -| to notice due to the introduction of conection certificate information, -| the item size being so much larger. Found and fixed by Wolfgang Breyha. -| -| +JH/06 Bug 1533: Fix truncation of items in headers_remove lists. A fixed -| + size buffer was used, resulting in syntax errors when an expansion -| + exceeded it. -| + -| + -| Exim version 4.84 -| ----------------- -| TL/01 Bugzilla 1506: Re-add a 'return NULL' to silence complaints from static -diff --git a/src/transport.c b/src/transport.c -index 31437b1..15c30bf 100644 ---- a/src/transport.c -+++ b/src/transport.c -@@ -643,8 +643,7 @@ for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) - { - int sep = ':'; /* This is specified as a colon-separated list */ - uschar *s, *ss; -- uschar buffer[128]; -- while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) -+ while ((s = string_nextinlist(&list, &sep, NULL, 0))) - { - int len; - --- -2.1.4 - diff -Nru exim4-4.84/debian/patches/84_Fix-truncation-of-items-in-headers_remove-lists-this.patch exim4-4.86~RC4/debian/patches/84_Fix-truncation-of-items-in-headers_remove-lists-this.patch --- exim4-4.84/debian/patches/84_Fix-truncation-of-items-in-headers_remove-lists-this.patch 2015-04-03 14:16:43.000000000 +0200 +++ exim4-4.86~RC4/debian/patches/84_Fix-truncation-of-items-in-headers_remove-lists-this.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,28 +0,0 @@ -From 97f83c7a669a525a5f5964a5c0708c311673b87f Mon Sep 17 00:00:00 2001 -From: Jeremy Harris -Date: Thu, 29 Jan 2015 17:42:47 +0000 -Subject: [PATCH] Fix truncation of items in headers_remove lists, this time in - routers. Bug 1533 - ---- - src/src/routers/rf_get_munge_headers.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/src/routers/rf_get_munge_headers.c b/src/routers/rf_get_munge_headers.c -index a4a13b0..3125f31 100644 ---- a/src/routers/rf_get_munge_headers.c -+++ b/src/routers/rf_get_munge_headers.c -@@ -90,9 +90,8 @@ if (rblock->remove_headers) - uschar * list = rblock->remove_headers; - int sep = ':'; - uschar * s; -- uschar buffer[128]; - -- while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) -+ while ((s = string_nextinlist(&list, &sep, NULL, 0))) - if (!(s = expand_string(s))) - { - if (!expand_string_forcedfail) --- -2.1.4 - diff -Nru exim4-4.84/debian/patches/fix_smtp_banner.patch exim4-4.86~RC4/debian/patches/fix_smtp_banner.patch --- exim4-4.84/debian/patches/fix_smtp_banner.patch 2015-01-14 12:26:06.000000000 +0100 +++ exim4-4.86~RC4/debian/patches/fix_smtp_banner.patch 2015-07-06 12:09:58.000000000 +0200 @@ -1,13 +1,13 @@ Description: Add EXIM_DISTRIBUTION var to display it on the SMTP banner Origin: https://blueprints.launchpad.net/ubuntu/+spec/servercloud-s-server-app-banner-updates Author: Yolanda Robla -Last-Update: 2013-06-20 +Last-Update: 2015-07-06 Index: b/src/globals.c =================================================================== --- a/src/globals.c +++ b/src/globals.c -@@ -1216,7 +1216,7 @@ +@@ -1250,7 +1250,7 @@ uschar *smtp_active_hostname = NULL; BOOL smtp_authenticated = FALSE; uschar *smtp_banner = US"$smtp_active_hostname ESMTP " @@ -20,7 +20,7 @@ =================================================================== --- a/src/config.h.defaults +++ b/src/config.h.defaults -@@ -199,4 +199,6 @@ +@@ -201,4 +201,6 @@ #define SC_EXIM_ARITH "%" SCNi64 /* scanf incl. 0x prefix */ #define SC_EXIM_DEC "%" SCNd64 /* scanf decimal */ @@ -48,8 +48,8 @@ =================================================================== --- a/src/exim.h +++ b/src/exim.h -@@ -591,4 +591,8 @@ - #endif +@@ -596,4 +596,8 @@ + #undef DISABLE_DNSSEC #endif +#ifndef EXIM_DISTRIBUTION diff -Nru exim4-4.84/debian/patches/series exim4-4.86~RC4/debian/patches/series --- exim4-4.84/debian/patches/series 2015-04-03 14:16:43.000000000 +0200 +++ exim4-4.86~RC4/debian/patches/series 2015-07-06 11:34:23.000000000 +0200 @@ -3,14 +3,10 @@ 33_eximon.binary.dpatch 34_eximstatsmanpage.dpatch 35_install.dpatch +40_reproducible_build.diff 50_localscan_dlopen.dpatch 60_convert4r4.dpatch 66_enlarge-dh-parameters-size.dpatch 67_unnecessaryCopt.diff 70_remove_exim-users_references.dpatch -80_mime_empty_charset.diff -81_buffer-overrun-in-spam-acl.diff -82_quoted-or-r-2047-encoded.diff -83_Remove-limit-on-remove_headers-item-size.-Bug-1533.patch -84_Fix-truncation-of-items-in-headers_remove-lists-this.patch fix_smtp_banner.patch diff -Nru exim4-4.84/debian/README.Debian.xml exim4-4.86~RC4/debian/README.Debian.xml --- exim4-4.84/debian/README.Debian.xml 2014-07-22 19:16:03.000000000 +0200 +++ exim4-4.86~RC4/debian/README.Debian.xml 2015-06-23 19:10:26.000000000 +0200 @@ -1148,13 +1148,14 @@ job will malfunction. - It might be appropriate to add "+tls_cipher +tls_peerdn" to + It might be appropriate to add "+tls_cipher" to any log_selector statement you might already have, or to add a log_selector statement setting these two options in a local - configuration file. These options have Exim log what cipher + configuration file. (For Debian's configuration simply define + the MAIN_LOG_SELECTOR macro.) + This option makes Exim log what cipher your Exim and the peer's mailer have negotiated to use to - encrypt the transaction, and they have Exim log the - Distinguished Name of the peer's certificate. + encrypt the transaction. Exim can be configured to ask a client for a certificate and to diff -Nru exim4-4.84/debian/rules exim4-4.86~RC4/debian/rules --- exim4-4.84/debian/rules 2014-11-18 18:58:37.000000000 +0100 +++ exim4-4.86~RC4/debian/rules 2015-06-27 14:25:59.000000000 +0200 @@ -110,6 +110,20 @@ DEBVERSION := $(shell dpkg-parsechangelog | sed -n '/^Version: /s/^Version: //p') UPSTREAMVERSION := $(shell echo $(DEBVERSION) | sed -n 's/\(.\+\)-[^-]\+/\1/p') MTACONFLICTS := $(shell cat $(DEBIAN)/mtalist) +DEBTIME := $(shell dpkg-parsechangelog --show-field Date) +REPBUILDDATE := \ + $(shell env LC_ALL=C TZ=UTC date --date="$(DEBTIME)" '+%b %e %Y') +REPBUILDTIME := \ + $(shell env LC_ALL=C TZ=UTC date --date="$(DEBTIME)" '+%H:%M:%S') + +PROVIDE_DEFAULT_MTA := $(shell if dpkg-vendor --is Ubuntu || \ + dpkg-vendor --derives-from Ubuntu ; then : ; else \ + echo "default-mta" ; fi) +# for reproducible build. If set exim would use $TZ as default value for +# TIMEZONE_DEFAULT +undefine TZ +unexport TZ + # set up build directory b-exim4-daemon-heavy/ $(addsuffix /Makefile,$(BDIRS)): %/Makefile: @@ -119,6 +133,10 @@ -name 'b-*' -o -print0 | \ xargs --no-run-if-empty --null \ cp -a --target-directory=$* + printf '#define REPBUILDDATE "$(REPBUILDDATE)"\n' \ + > $*/src/repbuildtime.h && \ + printf '#define REPBUILDTIME "$(REPBUILDTIME)"\n' \ + >> $*/src/repbuildtime.h unpack-configs: unpack-configs-stamp @@ -481,7 +499,8 @@ dh_shlibdeps $(dhbuildpackages) dh_gencontrol $(dhbuildpackages) -- \ -VUpstream-Version=$(UPSTREAMVERSION) \ - -VMTA-Conflicts="$(MTACONFLICTS)" + -VMTA-Conflicts="$(MTACONFLICTS)" \ + -Vdist:Provides:exim4-daemon-light="$(PROVIDE_DEFAULT_MTA)" dh_md5sums $(dhbuildpackages) dh_builddeb $(dhbuildpackages) diff -Nru exim4-4.84/debian/watch exim4-4.86~RC4/debian/watch --- exim4-4.84/debian/watch 2014-07-22 19:16:03.000000000 +0200 +++ exim4-4.86~RC4/debian/watch 2015-06-13 15:22:29.000000000 +0200 @@ -1,3 +1,3 @@ version=3 -opts=pgpsigurlmangle=s/$/.asc/ \ +opts=pgpsigurlmangle=s/$/.asc/,uversionmangle=s/_/~/ \ http://ftp.exim.org/pub/exim/exim4/exim-(\d.*)\.(?:tgz|tar\.(?:gz|bz2|xz)) diff -Nru exim4-4.84/doc/ChangeLog exim4-4.86~RC4/doc/ChangeLog --- exim4-4.84/doc/ChangeLog 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/doc/ChangeLog 2015-06-27 17:01:28.000000000 +0200 @@ -2,6 +2,217 @@ ------------------------------------------- +Exim version 4.86 +----------------- +JH/01 Bug 1545: The smtp transport option "retry_include_ip_address" is now + expanded. + +JH/02 The smtp transport option "multi_domain" is now expanded. + +JH/03 The smtp transport now requests PRDR by default, if the server offers + it. + +JH/04 Certificate name checking on server certificates, when exim is a client, + is now done by default. The transport option tls_verify_cert_hostnames + can be used to disable this per-host. The build option + EXPERIMENTAL_CERTNAMES is withdrawn. + +JH/05 The value of the tls_verify_certificates smtp transport and main options + default to the word "system" to access the system default CA bundle. + For GnuTLS, only version 3.0.20 or later. + +JH/06 Verification of the server certificate for a TLS connection is now tried + (but not required) by default. The verification status is now logged by + default, for both outbound TLS and client-certificate supplying inbound + TLS connections + +JH/07 Changed the default rfc1413 lookup settings to disable calls. Few + sites use this now. + +JH/08 The EXPERIMENTAL_DSN compile option is no longer needed; all Delivery + Status Notification (bounce) messages are now MIME format per RFC 3464. + Support for RFC 3461 DSN options NOTIFY,ENVID,RET,ORCPT can be advertised + under the control of the dsn_advertise_hosts option, and routers may + have a dsn_lasthop option. + +JH/09 A timeout of 2 minutes is now applied to all malware scanner types by + default, modifiable by a malware= option. The list separator for + the options can now be changed in the usual way. Bug 68. + +JH/10 The smtp_receive_timeout main option is now expanded before use. + +JH/11 The incoming_interface log option now also enables logging of the + local interface on delivery outgoing connections. + +JH/12 The cutthrough-routing facility now supports multi-recipient mails, + if the interface and destination host and port all match. + +JH/13 Bug 344: The verify = reverse_host_lookup ACL condition now accepts a + /defer_ok option. + +JH/14 Bug 1573: The spam= ACL condition now additionally supports Rspamd. + Patch from Andrew Lewis. + +JH/15 Bug 670: The spamd_address main option (for the spam= ACL condition) + now supports optional time-restrictions, weighting, and priority + modifiers per server. Patch originally by . + +JH/16 The spamd_address main option now supports a mixed list of local + and remote servers. Remote servers can be IPv6 addresses, and + specify a port-range. + +JH/17 Bug 68: The spamd_address main option now supports an optional + timeout value per server. + +JH/18 Bug 1581: Router and transport options headers_add/remove can + now have the list separator specified. + +JH/19 Bug 392: spamd_address, and clamd av_scanner, now support retry + option values. + +JH/20 Bug 1571: Ensure that $tls_in_peerdn is set, when verification fails + under OpenSSL. + +JH/21 Support for the A6 type of dns record is withdrawn. + +JH/22 Bug 608: The result of a QUIT or not-QUIT toplevel ACL now matters + rather than the verbs used. + +JH/23 Bug 1572: Increase limit on SMTP confirmation message copy size + from 255 to 1024 chars. + +JH/24 Verification callouts now attempt to use TLS by default. + +HS/01 DNSSEC options (dnssec_require_domains, dnssec_request_domains) + are generic router options now. The defaults didn't change. + +JH/25 Bug 466: Add RFC2322 support for MIME attachment filenames. + Original patch from Alexander Shikoff, worked over by JH. + +HS/02 Bug 1575: exigrep falls back to autodetection of compressed + files if ZCAT_COMMAND is not executable. + +JH/26 Bug 1539: Add timout/retry options on dnsdb lookups. + +JH/27 Bug 286: Support SOA lookup in dnsdb lookups. + +JH/28 Bug 1588: Do not use the A lookup following an AAAA for setting the FQDN. + Normally benign, it bites when the pair was led to by a CNAME; + modern usage is to not canoicalize the domain to a CNAME target + (and we were inconsistent anyway for A-only vs AAAA+A). + +JH/29 Bug 1632: Removed the word "rejected" from line logged for ACL discards. + +JH/30 Check the forward DNS lookup for DNSSEC, in addition to the reverse, + when evaluating $sender_host_dnssec. + +JH/31 Check the HELO verification lookup for DNSSEC, adding new + $sender_helo_dnssec variable. + +JH/32 Bug 1397: Enable ECDHE on OpenSSL, just the NIST P-256 curve. + +JH/33 Bug 1346: Note MAIL cmd seen in -bS batch, to avoid smtp_no_mail log. + +JH/34 Bug 1648: Fix a memory leak seen with "mailq" and large queues. + +JH/35 Bug 1642: Fix support of $spam_ variables at delivery time. Was + documented as working, but never had. Support all but $spam_report. + + +Exim version 4.85 +----------------- +TL/01 When running the test suite, the README says that variables such as + no_msglog_check are global and can be placed anywhere in a specific + test's script, however it was observed that placement needed to be near + the beginning for it to behave that way. Changed the runtest perl + script to read through the entire script once to detect and set these + variables, reset to the beginning of the script, and then run through + the script parsing/test process like normal. + +TL/02 The BSD's have an arc4random API. One of the functions to induce + adding randomness was arc4random_stir(), but it has been removed in + OpenBSD 5.5. Detect this OpenBSD version and skip calling this + function when detected. + +JH/01 Expand the EXPERIMENTAL_TPDA feature. Several different events now + cause callback expansion. + +TL/03 Bugzilla 1518: Clarify "condition" processing in routers; that + syntax errors in an expansion can be treated as a string instead of + logging or causing an error, due to the internal use of bool_lax + instead of bool when processing it. + +JH/02 Add EXPERIMENTAL_DANE, allowing for using the DNS as trust-anchor for + server certificates when making smtp deliveries. + +JH/03 Support secondary-separator specifier for MX, SRV, TLSA lookups. + +JH/04 Add ${sort {list}{condition}{extractor}} expansion item. + +TL/04 Bugzilla 1216: Add -M (related messages) option to exigrep. + +TL/05 GitHub Issue 18: Adjust logic testing for true/false in redis lookups. + Merged patch from Sebastian Wiedenroth. + +JH/05 Fix results-pipe from transport process. Several recipients, combined + with certificate use, exposed issues where response data items split + over buffer boundaries were not parsed properly. This eventually + resulted in duplicates being sent. This issue only became common enough + to notice due to the introduction of conection certificate information, + the item size being so much larger. Found and fixed by Wolfgang Breyha. + +JH/06 Bug 1533: Fix truncation of items in headers_remove lists. A fixed + size buffer was used, resulting in syntax errors when an expansion + exceeded it. + +JH/07 Add support for directories of certificates when compiled with a GnuTLS + version 3.3.6 or later. + +JH/08 Rename the TPDA expermimental facility to Event Actions. The #ifdef + is EXPERIMENTAL_EVENT, the main-configuration and transport options + both become "event_action", the variables become $event_name, $event_data + and $event_defer_errno. There is a new variable $verify_mode, usable in + routers, transports and related events. The tls:cert event is now also + raised for inbound connections, if the main configuration event_action + option is defined. + +TL/06 In test suite, disable OCSP for old versions of openssl which contained + early OCSP support, but no stapling (appears to be less than 1.0.0). + +JH/09 When compiled with OpenSSL and EXPERIMENTAL_CERTNAMES, the checks on + server certificate names available under the smtp transport option + "tls_verify_cert_hostname" now do not permit multi-component wildcard + matches. + +JH/10 Time-related extraction expansions from certificates now use the main + option "timezone" setting for output formatting, and are consistent + between OpenSSL and GnuTLS compilations. Bug 1541. + +JH/11 Fix a crash in mime ACL when meeting a zero-length, quoted or RFC2047- + encoded parameter in the incoming message. Bug 1558. + +JH/12 Bug 1527: Autogrow buffer used in reading spool files. Since they now + include certificate info, eximon was claiming there were spoolfile + syntax errors. + +JH/13 Bug 1521: Fix ldap lookup for single-attr request, multiple-attr return. + +JH/14 Log delivery-related information more consistently, using the sequence + "H= []" wherever possible. + +TL/07 Bug 1547: Omit RFCs from release. Draft and RFCs have licenses which + are problematic for Debian distribution, omit them from the release + tarball. + +JH/15 Updates and fixes to the EXPERIMENTAL_DSN feature. + +JH/16 Fix string representation of time values on 64bit time_t anchitectures. + Bug 1561. + +JH/17 Fix a null-indirection in certextract expansions when a nondefault + output list separator was used. + + Exim version 4.84 ----------------- TL/01 Bugzilla 1506: Re-add a 'return NULL' to silence complaints from static @@ -14,7 +225,7 @@ JH/02 Fix broken compilation when EXPERIMENTAL_DSN is enabled. TL/02 Bug 1509: Fix exipick for enhanced spoolfile specification used when - EXPERIMENTAL_DNS is enabled. Fix from Wolfgang Breyha. + EXPERIMENTAL_DSN is enabled. Fix from Wolfgang Breyha. Exim version 4.83 diff -Nru exim4-4.84/doc/DANE-draft-notes exim4-4.86~RC4/doc/DANE-draft-notes --- exim4-4.84/doc/DANE-draft-notes 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/doc/DANE-draft-notes 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,11 @@ + +draft 11 + +3.1.2 - Para 4 (records with Sel Full(0) are discouraged) +==> There's a matching type Full but not such a Selector type. + Should this be "Cert(0), or Matching Type Full(0)" ? + Suspect the latter. + +3.1.2 Needs a para added regarding certificate date verification, + to contrast with the requirement to NOT check for + DANE-EE defined in 3.1.1 diff -Nru exim4-4.84/doc/exim.8 exim4-4.86~RC4/doc/exim.8 --- exim4-4.84/doc/exim.8 2014-08-11 15:13:29.000000000 +0200 +++ exim4-4.86~RC4/doc/exim.8 2015-06-28 04:47:58.000000000 +0200 @@ -1003,6 +1003,11 @@ by Exim in conjunction with the \fB\-MC\fP option. It signifies that the connection to the remote host has been authenticated. .TP 10 +\fB\-MCD\fP +This option is not intended for use by external callers. It is used internally +by Exim in conjunction with the \fB\-MC\fP option. It signifies that the +remote host supports the ESMTP DSN extension. +.TP 10 \fB\-MCP\fP This option is not intended for use by external callers. It is used internally by Exim in conjunction with the \fB\-MC\fP option. It signifies that the server to @@ -1634,7 +1639,12 @@ \fB\-X\fP <\fIlogfile\fP> This option is interpreted by Sendmail to cause debug information to be sent to the named file. It is ignored by Exim. -.sp +.TP 10 +\fB\-z\fP <\fIlog\-line\fP> +This option writes its argument to Exim's logfile. +Use is restricted to administrators; the intent is for operational notes. +Quotes should be used to maintain a multi\-word item as a single argument, +under most shells. . .SH "SEE ALSO" .rs diff -Nru exim4-4.84/doc/experimental-spec.txt exim4-4.86~RC4/doc/experimental-spec.txt --- exim4-4.84/doc/experimental-spec.txt 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/doc/experimental-spec.txt 2015-06-27 17:01:28.000000000 +0200 @@ -623,10 +623,10 @@ configure a dmarc_forensic_sender because the default sender address construction might be inadequate. - control = dmarc_forensic_enable + control = dmarc_enable_forensic (AGAIN: You can choose not to send these forensic reports by simply -not putting the dmarc_forensic_enable control line at any point in +not putting the dmarc_enable_forensic control line at any point in your exim config. If you don't tell it to send them, it will not send them.) @@ -755,94 +755,109 @@ deny dmarc_status = reject !authenticated = * - message = Message from $domain_used_domain failed sender's DMARC policy, REJECT + message = Message from $dmarc_used_domain failed sender's DMARC policy, REJECT -Transport post-delivery actions +Event Actions -------------------------------------------------------------- -An arbitrary per-transport string can be expanded on successful delivery, -and (for SMTP transports) a second string on deferrals caused by a host error. +(Renamed from TPDA, Transport post-delivery actions) + +An arbitrary per-transport string can be expanded upon various transport events. +Additionally a main-section configuration option can be expanded on some +per-message events. This feature may be used, for example, to write exim internal log information (not available otherwise) into a database. -In order to use the feature, you must set +In order to use the feature, you must compile with -EXPERIMENTAL_TPDA=yes +EXPERIMENTAL_EVENT=yes in your Local/Makefile -and define the expandable strings in the runtime config file, to -be executed at end of delivery. - -Additionally, there are 6 more variables, available at end of -delivery: - -tpda_delivery_ip IP of host, which has accepted delivery -tpda_delivery_port Port of remote host which has accepted delivery -tpda_delivery_fqdn FQDN of host, which has accepted delivery -tpda_delivery_local_part local part of address being delivered -tpda_delivery_domain domain part of address being delivered -tpda_delivery_confirmation SMTP confirmation message - -In case of a deferral caused by a host-error: -tpda_defer_errno Error number -tpda_defer_errstr Error string possibly containing more details - -The $router_name and $transport_name variables are also usable. - +and define one or both of +- the event_action option in the transport +- the event_action main option +to be expanded when the event fires. + +A new variable, $event_name, is set to the event type when the +expansion is done. The current list of events is: + + msg:complete after main per message + msg:delivery after transport per recipient + msg:host:defer after transport per attempt + msg:fail:delivery after main per recipient + msg:fail:internal after main per recipient + tcp:connect before transport per connection + tcp:close after transport per connection + tls:cert before both per certificate in verification chain + smtp:connect after transport per connection + +The expansion is called for all event types, and should use the $event_name +variable to decide when to act. The value of the variable is a colon-separated +list, defining a position in the tree of possible events; it may be used as +a list or just matched on as a whole. There will be no whitespace. + + +There is an auxilary variable, $event_data, for which the +content is event_dependent: + + msg:delivery smtp confirmation mssage + msg:host:defer error string + tls:cert verification chain depth + smtp:connect smtp banner + +The msg:host:defer event populates one extra variable, $event_defer_errno. + +The following variables are likely to be useful depending on the event type: + + router_name, transport_name + local_part, domain + host, host_address, host_port + tls_out_peercert + lookup_dnssec_authenticated, tls_out_dane + sending_ip_address, sending_port + message_exim_id, verify_mode -To take action after successful deliveries, set the following option -on any transport of interest. - -tpda_delivery_action An example might look like: -tpda_delivery_action = \ -${lookup pgsql {SELECT * FROM record_Delivery( \ +event_action = ${if eq {msg:delivery}{$event_name} \ +{${lookup pgsql {SELECT * FROM record_Delivery( \ '${quote_pgsql:$sender_address_domain}',\ '${quote_pgsql:${lc:$sender_address_local_part}}', \ - '${quote_pgsql:$tpda_delivery_domain}', \ - '${quote_pgsql:${lc:$tpda_delivery_local_part}}', \ - '${quote_pgsql:$tpda_delivery_ip}', \ - '${quote_pgsql:${lc:$tpda_delivery_fqdn}}', \ - '${quote_pgsql:$message_exim_id}')}} + '${quote_pgsql:$domain}', \ + '${quote_pgsql:${lc:$local_part}}', \ + '${quote_pgsql:$host_address}', \ + '${quote_pgsql:${lc:$host}}', \ + '${quote_pgsql:$message_exim_id}')}} \ +} {}} -The string is expanded after the delivery completes and any -side-effects will happen. The result is then discarded. +The string is expanded when each of the supported events occur +and any side-effects of the expansion will happen. Note that for complex operations an ACL expansion can be used. -In order to log host deferrals, add the following option to an SMTP -transport: - -tpda_host_defer_action - -This is a private option of the SMTP transport. It is intended to -log failures of remote hosts. It is executed only when exim has -attempted to deliver a message to a remote host and failed due to -an error which doesn't seem to be related to the individual -message, sender, or recipient address. -See section 47.2 of the exim documentation for more details on how -this is determined. - -Example: - -tpda_host_defer_action = \ -${lookup mysql {insert into delivlog set \ - msgid = '${quote_mysql:$message_exim_id}', \ - senderlp = '${quote_mysql:${lc:$sender_address_local_part}}', \ - senderdom = '${quote_mysql:$sender_address_domain}', \ - delivlp = '${quote_mysql:${lc:$tpda_delivery_local_part}}', \ - delivdom = '${quote_mysql:$tpda_delivery_domain}', \ - delivip = '${quote_mysql:$tpda_delivery_ip}', \ - delivport = '${quote_mysql:$tpda_delivery_port}', \ - delivfqdn = '${quote_mysql:$tpda_delivery_fqdn}', \ - deliverrno = '${quote_mysql:$tpda_defer_errno}', \ - deliverrstr = '${quote_mysql:$tpda_defer_errstr}' \ - }} +The expansion of the event_action option should normally +return an empty string. Should it return anything else the +following will be forced: + + msg:delivery (ignored) + msg:host:defer (ignored) + msg:fail:delivery (ignored) + tcp:connect do not connect + tcp:close (ignored) + tls:cert refuse verification + smtp:connect close connection + +No other use is made of the result string. + + +Known issues: +- the tls:cert event is only called for the cert chain elements + received over the wire, with GnuTLS. OpenSSL gives the entire + chain including those loaded locally. Redis Lookup @@ -1071,81 +1086,287 @@ 221 mail.example.net closing connection -DSN Support --------------------------------------------------------------- - -DSN Support tries to add RFC 3461 support to Exim. It adds support for -*) the additional parameters for MAIL FROM and RCPT TO -*) RFC complient MIME DSN messages for all of - success, failure and delay notifications -*) dsn_advertise_hosts main option to select which hosts are able - to use the extension -*) dsn_lasthop router switch to end DSN processing - -In case of failure reports this means that the last three parts, the message body -intro, size info and final text, of the defined template are ignored since there is no -logical place to put them in the MIME message. -All the other changes are made without changing any defaults -Building exim: --------------- +SOCKS +------------------------------------------------------------ +Support for proxying outbound SMTP via a Socks 5 proxy +(RFC 1928) is included if Exim is compiled with +EXPERIMENTAL_SOCKS defined. + +If an smtp transport has a nonempty socks_proxy option +defined, this is active. The option is expanded and +should be a list (colon-separated by default) of +proxy specifiers. Each proxy specifier is a list +(space-separated by default) where the initial element +is an IP address and any subsequent elements are options. + +Options are a string =. +These options are currently defined: +- "auth", with possible values "none" and "name". + Using "name" selects username/password authentication + per RFC 1929. Default is "none". +- "name" sets the authentication username. Default is empty. +- "pass" sets the authentication password. Default is empty. +- "port" sets the tcp port number for the proxy. Default is 1080. +- "tmo" sets a connection timeout in seconds for this proxy. Default is 5. + +Proxies from the list are tried in order until +one responds. The timeout for the overall connection +applies to the set of proxied attempts. + +If events are used, the remote IP/port during a +tcp:connect event will be that of the proxy. + + + + +DANE +------------------------------------------------------------ +DNS-based Authentication of Named Entities, as applied +to SMTP over TLS, provides assurance to a client that +it is actually talking to the server it wants to rather +than some attacker operating a Man In The Middle (MITM) +operation. The latter can terminate the TLS connection +you make, and make another one to the server (so both +you and the server still think you have an encrypted +connection) and, if one of the "well known" set of +Certificate Authorities has been suborned - something +which *has* been seen already (2014), a verifiable +certificate (if you're using normal root CAs, eg. the +Mozilla set, as your trust anchors). + +What DANE does is replace the CAs with the DNS as the +trust anchor. The assurance is limited to a) the possibility +that the DNS has been suborned, b) mistakes made by the +admins of the target server. The attack surface presented +by (a) is thought to be smaller than that of the set +of root CAs. + +It also allows the server to declare (implicitly) that +connections to it should use TLS. An MITM could simply +fail to pass on a server's STARTTLS. + +DANE scales better than having to maintain (and +side-channel communicate) copies of server certificates +for every possible target server. It also scales +(slightly) better than having to maintain on an SMTP +client a copy of the standard CAs bundle. It also +means not having to pay a CA for certificates. + +DANE requires a server operator to do three things: +1) run DNSSEC. This provides assurance to clients +that DNS lookups they do for the server have not +been tampered with. The domain MX record applying +to this server, its A record, its TLSA record and +any associated CNAME records must all be covered by +DNSSEC. +2) add TLSA DNS records. These say what the server +certificate for a TLS connection should be. +3) offer a server certificate, or certificate chain, +in TLS connections which is traceable to the one +defined by (one of?) the TSLA records + +There are no changes to Exim specific to server-side +operation of DANE. + +The TLSA record for the server may have "certificate +usage" of DANE-TA(2) or DANE-EE(3). The latter specifies +the End Entity directly, i.e. the certificate involved +is that of the server (and should be the sole one transmitted +during the TLS handshake); this is appropriate for a +single system, using a self-signed certificate. + DANE-TA usage is effectively declaring a specific CA +to be used; this might be a private CA or a public, +well-known one. A private CA at simplest is just +a self-signed certificate which is used to sign +cerver certificates, but running one securely does +require careful arrangement. If a private CA is used +then either all clients must be primed with it, or +(probably simpler) the server TLS handshake must transmit +the entire certificate chain from CA to server-certificate. +If a public CA is used then all clients must be primed with it +(losing one advantage of DANE) - but the attack surface is +reduced from all public CAs to that single CA. +DANE-TA is commonly used for several services and/or +servers, each having a TLSA query-domain CNAME record, +all of which point to a single TLSA record. + +The TLSA record should have a Selector field of SPKI(1) +and a Matching Type field of SHA2-512(2). + +At the time of writing, https://www.huque.com/bin/gen_tlsa +is useful for quickly generating TLSA records; and commands like + + openssl x509 -in -pubkey -noout /dev/null \ + | openssl sha512 \ + | awk '{print $2}' + +are workable for 4th-field hashes. + +For use with the DANE-TA model, server certificates +must have a correct name (SubjectName or SubjectAltName). + +The use of OCSP-stapling should be considered, allowing +for fast revocation of certificates (which would otherwise +be limited by the DNS TTL on the TLSA records). However, +this is likely to only be usable with DANE-TA. NOTE: the +default of requesting OCSP for all hosts is modified iff +DANE is in use, to: + + hosts_request_ocsp = ${if or { {= {0}{$tls_out_tlsa_usage}} \ + {= {4}{$tls_out_tlsa_usage}} } \ + {*}{}} + +The (new) variable $tls_out_tlsa_usage is a bitfield with +numbered bits set for TLSA record usage codes. +The zero above means DANE was not in use, +the four means that only DANE-TA usage TLSA records were +found. If the definition of hosts_request_ocsp includes the +string "tls_out_tlsa_usage", they are re-expanded in time to +control the OCSP request. + +This modification of hosts_request_ocsp is only done if +it has the default value of "*". Admins who change it, and +those who use hosts_require_ocsp, should consider the interaction +with DANE in their OCSP settings. + + +For client-side DANE there are two new smtp transport options, +hosts_try_dane and hosts_require_dane. They do the obvious thing. +[ should they be domain-based rather than host-based? ] + +DANE will only be usable if the target host has DNSSEC-secured +MX, A and TLSA records. + +A TLSA lookup will be done if either of the above options match +and the host-lookup succeded using dnssec. +If a TLSA lookup is done and succeeds, a DANE-verified TLS connection +will be required for the host. + +(TODO: specify when fallback happens vs. when the host is not used) + +If DANE is requested and useable (see above) the following transport +options are ignored: + hosts_require_tls + tls_verify_hosts + tls_try_verify_hosts + tls_verify_certificates + tls_crl + tls_verify_cert_hostnames + +If DANE is not usable, whether requested or not, and CA-anchored +verification evaluation is wanted, the above variables should be set +appropriately. + +Currently dnssec_request_domains must be active (need to think about that) +and dnssec_require_domains is ignored. + +If verification was successful using DANE then the "CV" item +in the delivery log line will show as "CV=dane". + +There is a new variable $tls_out_dane which will have "yes" if +verification succeeded using DANE and "no" otherwise (only useful +in combination with EXPERIMENTAL_EVENT), and a new variable +$tls_out_tlsa_usage (detailed above). + + + +INTERNATIONAL +------------------------------------------------------------ +SMTPUTF8 +Internationalised mail name handling. +RFCs 6530, 6533, 5890 + +Compile with EXPERIMENTAL_INTERNATIONAL and libidn. + +New main config option smtputf8_advertise_hosts, default '*', +a host list. If this matches the sending host and +accept_8bitmime is true (the default) then the ESMTP option +SMTPUTF8 will be advertised. + +If the sender specifies the SMTPUTF8 option on a MAIL command +international handling for the message is enabled and +the expansion variable $message_smtputf8 will have value TRUE. + +The option allow_utf8_domains is set to true for this +message. All DNS lookups are converted to a-label form +whatever the setting of allow_utf8_domains. + +Both localparts and domain are maintained as the original +utf8 form internally; any matching or regex use will +require appropriate care. Filenames created, eg. by +the appendfile transport, will have utf8 name. + +Helo names sent by the smtp transport will have any utf8 +components expanded to a-label form. + +Any certificate name checks will be done using the a-label +form of the name. + +Log lines and Received-by: header lines will aquire a "utf8" +prefix on the protocol element, eg. utf8esmtp. + +New expansion operators: + ${utf8_domain_to_alabel:str} + ${utf8_domain_from_alabel:str} + ${utf8_localpart_to_alabel:str} + ${utf8_localpart_from_alabel:str} + +New "control = utf8_downconvert" ACL modifier, +sets a flag requiring that addresses are converted to +a-label form before smtp delivery, for use in a +Message Submission Agent context. Can also be +phrased as "control = utf8_downconvert/1" and is +mandatory. The flag defaults to zero and can be cleared +by "control = utf8_downconvert/0". The value "-1" +may also be used, to use a-label for only if the +destination host does not support SMTPUTF8. + +If mua_wrapper is set, the utf8_downconvert control +defaults to -1 (convert if needed). + + +There is no explicit support for VRFY and EXPN. +Configurations supporting these should inspect +$smtp_command_argument for an SMTPUTF8 argument. + +There is no support for LMTP on Unix sockets. +Using the "lmtp" protocol option on an smtp transport, +for LMTP over TCP, should work as expected. + +Known issues: + - DSN unitext handling is not present + - no provision for converting logging from or to UTF-8 + +---- +IMAP folder names + +New expansion operator: + +${imapfolder {} {} {}} + +The string is converted from the charset specified by the headers charset +command (in a filter file) or headers_charset global option, to the +modified UTF-7 encoding specified by RFC 2060, with the following +exception: All occurences of (which has to be a single character) +are replaced with periods ("."), and all periods and slashes that aren't + and are not in the string are BASE64 encoded. + +The third argument can be omitted, defaulting to an empty string. +The second argument can be omitted, defaulting to "/". + +This is the encoding used by Courier for Maildir names on disk, and followed +by many other IMAP servers. + + Example 1: ${imapfolder {Foo/Bar}} yields "Foo.Bar". + Example 2: ${imapfolder {Foo/Bar}{.}{/}} yields "Foo&AC8-Bar". + Example 3: ${imapfolder {Räksmörgås}} yields "R&AOQ-ksm&APY-rg&AOU-s". -Define -EXPERIMENTAL_DSN=YES -in your Local/Makefile. +Note that the source charset setting is vital, and also that characters +must be representable in UTF-16. -Configuration: --------------- -All DSNs are sent in MIME format if you built exim with EXPERIMENTAL_DSN=YES -No option needed to activate it, and no way to turn it off. - -Failure and delay DSNs are triggered as usual except a sender used NOTIFY=... -to prevent them. - -Support for Success DSNs is added and activated by NOTIFY=SUCCESS by clients. - -Add -dsn_advertise_hosts = * -or a more restrictive host_list to announce DSN in EHLO answers - -Those hosts can then use NOTIFY,ENVID,RET,ORCPT options. - -If a message is relayed to a DSN aware host without changing the envelope -recipient the options are passed along and no success DSN is generated. - -A redirect router will always trigger a success DSN if requested and the DSN -options are not passed any further. - -A success DSN always contains the recipient address as submitted by the -client as required by RFC. Rewritten addresses are never exposed. - -If you used DSN patch up to 1.3 before remove all "dsn_process" switches from -your routers since you don't need them anymore. There is no way to "gag" -success DSNs anymore. Announcing DSN means answering as requested. - -You can prevent Exim from passing DSN options along to other DSN aware hosts by defining -dsn_lasthop -in a router. Exim will then send the success DSN himself if requested as if -the next hop does not support DSN. -Adding it to a redirect router makes no difference. - -Certificate name checking --------------------------------------------------------------- -The X509 certificates used for TLS are supposed be verified -that they are owned by the expected host. The coding of TLS -support to date has not made these checks. - -If built with EXPERIMENTAL_CERTNAMES defined, code is -included to do so, and a new smtp transport option -"tls_verify_cert_hostname" supported which takes a list of -names for which the checks must be made. The host must -also be in "tls_verify_hosts". - -Both Subject and Subject-Alternate-Name certificate fields -are supported, as are wildcard certificates (limited to -a single wildcard being the initial component of a 3-or-more -component FQDN). diff -Nru exim4-4.84/doc/filter.txt exim4-4.86~RC4/doc/filter.txt --- exim4-4.84/doc/filter.txt 2014-08-11 15:13:29.000000000 +0200 +++ exim4-4.86~RC4/doc/filter.txt 2015-06-28 04:47:58.000000000 +0200 @@ -5,9 +5,9 @@ Copyright (c) 2014 University of Cambridge +-----------------------------------------------------------------------------+ -+-------------------------------------+--------------------------------+------+ -|Revision 4.84 |11 Aug 2014 |PH | -+-------------------------------------+--------------------------------+------+ +|-----------------------------------------------------------------------------| +|Revision 4.86 |27 Jun 2015 |PH | ++-----------------------------------------------------------------------------+ ------------------------------------------------------------------------------- TABLE OF CONTENTS @@ -79,7 +79,7 @@ This document describes the user interfaces to Exim's in-built mail filtering facilities, and is copyright (c) University of Cambridge 2014. It corresponds -to Exim version 4.84. +to Exim version 4.86. 1.1 Introduction diff -Nru exim4-4.84/doc/NewStuff exim4-4.86~RC4/doc/NewStuff --- exim4-4.84/doc/NewStuff 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/doc/NewStuff 2015-06-27 17:01:28.000000000 +0200 @@ -6,6 +6,64 @@ test from the snapshots or the CVS before the documentation is updated. Once the documentation is updated, this file is reduced to a short list. +Version 4.86 +------------ + + 1. Support for using the system standard CA bundle. + + 2. New expansion items $config_file, $config_dir, containing the file + and directory name of the main configuration file. Also $exim_version. + + 3. New "malware=" support for Avast. + + 4. New "spam=" variant option for Rspamd. + + 5. Assorted options on malware= and spam= scanners. + + 6. A commandline option to write a comment into the logfile. + + 7. If built with EXPERIMENTAL_SOCKS feature enabled, the smtp transport can + be configured to make connections via socks5 proxies. + + 8. If built with EXPERIMENTAL_INTERNATIONAL, support is included for + the transmission of UTF-8 envelope addresses. + + 9. If built with EXPERIMENTAL_INTERNATIONAL, an expansion item for a commonly + used encoding of Maildir folder names. + +10. A logging option for slow DNS lookups. + +11. New ${env {}} expansion. + +12. A non-SMTP authenticator using information from TLS client certificates. + +13. Main option "tls_eccurve" for selecting an Elliptic Curve for TLS. + Patch originally by Wolfgang Breyha. + +14. Main option "dns_trust_aa" for trusting your local nameserver at the + same level as DNSSEC. + + +Version 4.85 +------------ + + 1. If built with EXPERIMENTAL_DANE feature enabled, Exim will follow the + DANE smtp draft to assess a secure chain of trust of the certificate + used to establish the TLS connection based on a TLSA record in the + domain of the sender. + + 2. The EXPERIMENTAL_TPDA feature has been renamed to EXPERIMENTAL_EVENT + and several new events have been created. The reason is because it has + been expanded beyond just firing events during the transport phase. Any + existing TPDA transport options will have to be rewritten to use a new + $event_name expansion variable in a condition. Refer to the + experimental-spec.txt for details and examples. + + 3. The EXPERIMENTAL_CERTNAMES features is an enhancement to verify that + server certs used for TLS match the result of the MX lookup. It does + not use the same mechanism as DANE. + + Version 4.84 ------------ @@ -28,7 +86,7 @@ 4. New malware type "sock". Talks over a Unix or TCP socket, sending one command line and matching a regex against the return data for trigger - and a second regex to extract malware_name. The mail spoofile name can + and a second regex to extract malware_name. The mail spoolfile name can be included in the command line. 5. The smtp transport now supports options "tls_verify_hosts" and diff -Nru exim4-4.84/doc/OptionLists.txt exim4-4.86~RC4/doc/OptionLists.txt --- exim4-4.84/doc/OptionLists.txt 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/doc/OptionLists.txt 2015-06-27 17:01:28.000000000 +0200 @@ -181,6 +181,7 @@ dns_csa_search_limit integer 5 main 4.60 dns_csa_use_reverse boolean true main 4.60 dns_dnssec_ok integer -1 main 4.82 +dns_dane_ok integer -1 main 4.83 dns_ipv4_lookup boolean false main 3.20 dns_qualify_single boolean true smtp dns_retrans time 0s main 1.60 diff -Nru exim4-4.84/doc/spec.txt exim4-4.86~RC4/doc/spec.txt --- exim4-4.84/doc/spec.txt 2014-08-11 15:13:27.000000000 +0200 +++ exim4-4.86~RC4/doc/spec.txt 2015-06-28 04:47:53.000000000 +0200 @@ -2,12 +2,12 @@ Exim Maintainers -Copyright (c) 2014 University of Cambridge +Copyright (c) 2015 University of Cambridge +-----------------------------------------------------------------------------+ -+-------------------------------------+--------------------------------+------+ -|Revision 4.84 |11 Aug 2014 |EM | -+-------------------------------------+--------------------------------+------+ +|-----------------------------------------------------------------------------| +|Revision 4.86 |27 Jun 2015 |EM | ++-----------------------------------------------------------------------------+ ------------------------------------------------------------------------------- TABLE OF CONTENTS @@ -125,21 +125,22 @@ 9.8. Lookup caching 9.9. Quoting lookup data 9.10. More about dnsdb - 9.11. Pseudo dnsdb record types - 9.12. Multiple dnsdb lookups - 9.13. More about LDAP - 9.14. Format of LDAP queries - 9.15. LDAP quoting - 9.16. LDAP connections - 9.17. LDAP authentication and control information - 9.18. Format of data returned by LDAP - 9.19. More about NIS+ - 9.20. SQL lookups - 9.21. More about MySQL, PostgreSQL, Oracle, and InterBase - 9.22. Specifying the server in the query - 9.23. Special MySQL features - 9.24. Special PostgreSQL features - 9.25. More about SQLite + 9.11. Dnsdb lookup modifiers + 9.12. Pseudo dnsdb record types + 9.13. Multiple dnsdb lookups + 9.14. More about LDAP + 9.15. Format of LDAP queries + 9.16. LDAP quoting + 9.17. LDAP connections + 9.18. LDAP authentication and control information + 9.19. Format of data returned by LDAP + 9.20. More about NIS+ + 9.21. SQL lookups + 9.22. More about MySQL, PostgreSQL, Oracle, and InterBase + 9.23. Specifying the server in the query + 9.24. Special MySQL features + 9.25. Special PostgreSQL features + 9.26. More about SQLite 10. Domain, host, address, and local part lists @@ -369,246 +370,247 @@ 40.1. Using spa as a server 40.2. Using spa as a client -41. Encrypted SMTP connections using TLS/SSL +41. The tls authenticator +42. Encrypted SMTP connections using TLS/SSL - 41.1. Support for the legacy "ssmtp" (aka "smtps") protocol - 41.2. OpenSSL vs GnuTLS - 41.3. GnuTLS parameter computation - 41.4. Requiring specific ciphers in OpenSSL - 41.5. Requiring specific ciphers or other parameters in GnuTLS - 41.6. Configuring an Exim server to use TLS - 41.7. Requesting and verifying client certificates - 41.8. Revoked certificates - 41.9. Configuring an Exim client to use TLS - 41.10. Use of TLS Server Name Indication - 41.11. Multiple messages on the same encrypted TCP/IP connection - 41.12. Certificates and all that - 41.13. Certificate chains - 41.14. Self-signed certificates - -42. Access control lists - - 42.1. Testing ACLs - 42.2. Specifying when ACLs are used - 42.3. The non-SMTP ACLs - 42.4. The SMTP connect ACL - 42.5. The EHLO/HELO ACL - 42.6. The DATA ACLs - 42.7. The SMTP DKIM ACL - 42.8. The SMTP MIME ACL - 42.9. The SMTP PRDR ACL - 42.10. The QUIT ACL - 42.11. The not-QUIT ACL - 42.12. Finding an ACL to use - 42.13. ACL return codes - 42.14. Unset ACL options - 42.15. Data for message ACLs - 42.16. Data for non-message ACLs - 42.17. Format of an ACL - 42.18. ACL verbs - 42.19. ACL variables - 42.20. Condition and modifier processing - 42.21. ACL modifiers - 42.22. Use of the control modifier - 42.23. Summary of message fixup control - 42.24. Adding header lines in ACLs - 42.25. Removing header lines in ACLs - 42.26. ACL conditions - 42.27. Using DNS lists - 42.28. Specifying the IP address for a DNS list lookup - 42.29. DNS lists keyed on domain names - 42.30. Multiple explicit keys for a DNS list - 42.31. Data returned by DNS lists - 42.32. Variables set from DNS lists - 42.33. Additional matching conditions for DNS lists - 42.34. Negated DNS matching conditions - 42.35. Handling multiple DNS records from a DNS list - 42.36. Detailed information from merged DNS lists - 42.37. DNS lists and IPv6 - 42.38. Rate limiting incoming messages - 42.39. Ratelimit options for what is being measured - 42.40. Ratelimit update modes - 42.41. Ratelimit options for handling fast clients - 42.42. Limiting the rate of different events - 42.43. Using rate limiting - 42.44. Address verification - 42.45. Callout verification - 42.46. Additional parameters for callouts - 42.47. Callout caching - 42.48. Sender address verification reporting - 42.49. Redirection while verifying - 42.50. Client SMTP authorization (CSA) - 42.51. Bounce address tag validation - 42.52. Using an ACL to control relaying - 42.53. Checking a relay configuration - -43. Content scanning at ACL time - - 43.1. Scanning for viruses - 43.2. Scanning with SpamAssassin - 43.3. Calling SpamAssassin from an Exim ACL - 43.4. Scanning MIME parts - 43.5. Scanning with regular expressions - 43.6. The demime condition - -44. Adding a local scan function to Exim - - 44.1. Building Exim to use a local scan function - 44.2. API for local_scan() - 44.3. Configuration options for local_scan() - 44.4. Available Exim variables - 44.5. Structure of header lines - 44.6. Structure of recipient items - 44.7. Available Exim functions - 44.8. More about Exim's memory handling - -45. System-wide message filtering - - 45.1. Specifying a system filter - 45.2. Testing a system filter - 45.3. Contents of a system filter - 45.4. Additional variable for system filters - 45.5. Defer, freeze, and fail commands for system filters - 45.6. Adding and removing headers in a system filter - 45.7. Setting an errors address in a system filter - 45.8. Per-address filtering - -46. Message processing - - 46.1. Submission mode for non-local messages - 46.2. Line endings - 46.3. Unqualified addresses - 46.4. The UUCP From line - 46.5. Resent- header lines - 46.6. The Auto-Submitted: header line - 46.7. The Bcc: header line - 46.8. The Date: header line - 46.9. The Delivery-date: header line - 46.10. The Envelope-to: header line - 46.11. The From: header line - 46.12. The Message-ID: header line - 46.13. The Received: header line - 46.14. The References: header line - 46.15. The Return-path: header line - 46.16. The Sender: header line - 46.17. Adding and removing header lines in routers and transports - 46.18. Constructed addresses - 46.19. Case of local parts - 46.20. Dots in local parts - 46.21. Rewriting addresses - -47. SMTP processing - - 47.1. Outgoing SMTP and LMTP over TCP/IP - 47.2. Errors in outgoing SMTP - 47.3. Incoming SMTP messages over TCP/IP - 47.4. Unrecognized SMTP commands - 47.5. Syntax and protocol errors in SMTP commands - 47.6. Use of non-mail SMTP commands - 47.7. The VRFY and EXPN commands - 47.8. The ETRN command - 47.9. Incoming local SMTP - 47.10. Outgoing batched SMTP - 47.11. Incoming batched SMTP - -48. Customizing bounce and warning messages - - 48.1. Customizing bounce messages - 48.2. Customizing warning messages - -49. Some common configuration settings - - 49.1. Sending mail to a smart host - 49.2. Using Exim to handle mailing lists - 49.3. Syntax errors in mailing lists - 49.4. Re-expansion of mailing lists - 49.5. Closed mailing lists - 49.6. Variable Envelope Return Paths (VERP) - 49.7. Virtual domains - 49.8. Multiple user mailboxes - 49.9. Simplified vacation processing - 49.10. Taking copies of mail - 49.11. Intermittently connected hosts - 49.12. Exim on the upstream server host - 49.13. Exim on the intermittently connected client host - -50. Using Exim as a non-queueing client -51. Log files - - 51.1. Where the logs are written - 51.2. Logging to local files that are periodically "cycled" - 51.3. Datestamped log files - 51.4. Logging to syslog - 51.5. Log line flags - 51.6. Logging message reception - 51.7. Logging deliveries - 51.8. Discarded deliveries - 51.9. Deferred deliveries - 51.10. Delivery failures - 51.11. Fake deliveries - 51.12. Completion - 51.13. Summary of Fields in Log Lines - 51.14. Other log entries - 51.15. Reducing or increasing what is logged - 51.16. Message log - -52. Exim utilities - - 52.1. Finding out what Exim processes are doing (exiwhat) - 52.2. Selective queue listing (exiqgrep) - 52.3. Summarizing the queue (exiqsumm) - 52.4. Extracting specific information from the log (exigrep) - 52.5. Selecting messages by various criteria (exipick) - 52.6. Cycling log files (exicyclog) - 52.7. Mail statistics (eximstats) - 52.8. Checking access policy (exim_checkaccess) - 52.9. Making DBM files (exim_dbmbuild) - 52.10. Finding individual retry times (exinext) - 52.11. Hints database maintenance - 52.12. exim_dumpdb - 52.13. exim_tidydb - 52.14. exim_fixdb - 52.15. Mailbox maintenance (exim_lock) - -53. The Exim monitor - - 53.1. Running the monitor - 53.2. The stripcharts - 53.3. Main action buttons - 53.4. The log display - 53.5. The queue display - 53.6. The queue menu - -54. Security considerations - - 54.1. Building a more "hardened" Exim - 54.2. Root privilege - 54.3. Running Exim without privilege - 54.4. Delivering to local files - 54.5. Running local commands - 54.6. Trust in configuration data - 54.7. IPv4 source routing - 54.8. The VRFY, EXPN, and ETRN commands in SMTP - 54.9. Privileged users - 54.10. Spool files - 54.11. Use of argv[0] - 54.12. Use of %f formatting - 54.13. Embedded Exim path - 54.14. Dynamic module directory - 54.15. Use of sprintf() - 54.16. Use of debug_printf() and log_write() - 54.17. Use of strcat() and strcpy() - -55. Format of spool files + 42.1. Support for the legacy "ssmtp" (aka "smtps") protocol + 42.2. OpenSSL vs GnuTLS + 42.3. GnuTLS parameter computation + 42.4. Requiring specific ciphers in OpenSSL + 42.5. Requiring specific ciphers or other parameters in GnuTLS + 42.6. Configuring an Exim server to use TLS + 42.7. Requesting and verifying client certificates + 42.8. Revoked certificates + 42.9. Configuring an Exim client to use TLS + 42.10. Use of TLS Server Name Indication + 42.11. Multiple messages on the same encrypted TCP/IP connection + 42.12. Certificates and all that + 42.13. Certificate chains + 42.14. Self-signed certificates + +43. Access control lists + + 43.1. Testing ACLs + 43.2. Specifying when ACLs are used + 43.3. The non-SMTP ACLs + 43.4. The SMTP connect ACL + 43.5. The EHLO/HELO ACL + 43.6. The DATA ACLs + 43.7. The SMTP DKIM ACL + 43.8. The SMTP MIME ACL + 43.9. The SMTP PRDR ACL + 43.10. The QUIT ACL + 43.11. The not-QUIT ACL + 43.12. Finding an ACL to use + 43.13. ACL return codes + 43.14. Unset ACL options + 43.15. Data for message ACLs + 43.16. Data for non-message ACLs + 43.17. Format of an ACL + 43.18. ACL verbs + 43.19. ACL variables + 43.20. Condition and modifier processing + 43.21. ACL modifiers + 43.22. Use of the control modifier + 43.23. Summary of message fixup control + 43.24. Adding header lines in ACLs + 43.25. Removing header lines in ACLs + 43.26. ACL conditions + 43.27. Using DNS lists + 43.28. Specifying the IP address for a DNS list lookup + 43.29. DNS lists keyed on domain names + 43.30. Multiple explicit keys for a DNS list + 43.31. Data returned by DNS lists + 43.32. Variables set from DNS lists + 43.33. Additional matching conditions for DNS lists + 43.34. Negated DNS matching conditions + 43.35. Handling multiple DNS records from a DNS list + 43.36. Detailed information from merged DNS lists + 43.37. DNS lists and IPv6 + 43.38. Rate limiting incoming messages + 43.39. Ratelimit options for what is being measured + 43.40. Ratelimit update modes + 43.41. Ratelimit options for handling fast clients + 43.42. Limiting the rate of different events + 43.43. Using rate limiting + 43.44. Address verification + 43.45. Callout verification + 43.46. Additional parameters for callouts + 43.47. Callout caching + 43.48. Sender address verification reporting + 43.49. Redirection while verifying + 43.50. Client SMTP authorization (CSA) + 43.51. Bounce address tag validation + 43.52. Using an ACL to control relaying + 43.53. Checking a relay configuration + +44. Content scanning at ACL time + + 44.1. Scanning for viruses + 44.2. Scanning with SpamAssassin and Rspamd + 44.3. Calling SpamAssassin from an Exim ACL + 44.4. Scanning MIME parts + 44.5. Scanning with regular expressions + 44.6. The demime condition + +45. Adding a local scan function to Exim + + 45.1. Building Exim to use a local scan function + 45.2. API for local_scan() + 45.3. Configuration options for local_scan() + 45.4. Available Exim variables + 45.5. Structure of header lines + 45.6. Structure of recipient items + 45.7. Available Exim functions + 45.8. More about Exim's memory handling + +46. System-wide message filtering + + 46.1. Specifying a system filter + 46.2. Testing a system filter + 46.3. Contents of a system filter + 46.4. Additional variable for system filters + 46.5. Defer, freeze, and fail commands for system filters + 46.6. Adding and removing headers in a system filter + 46.7. Setting an errors address in a system filter + 46.8. Per-address filtering + +47. Message processing + + 47.1. Submission mode for non-local messages + 47.2. Line endings + 47.3. Unqualified addresses + 47.4. The UUCP From line + 47.5. Resent- header lines + 47.6. The Auto-Submitted: header line + 47.7. The Bcc: header line + 47.8. The Date: header line + 47.9. The Delivery-date: header line + 47.10. The Envelope-to: header line + 47.11. The From: header line + 47.12. The Message-ID: header line + 47.13. The Received: header line + 47.14. The References: header line + 47.15. The Return-path: header line + 47.16. The Sender: header line + 47.17. Adding and removing header lines in routers and transports + 47.18. Constructed addresses + 47.19. Case of local parts + 47.20. Dots in local parts + 47.21. Rewriting addresses + +48. SMTP processing + + 48.1. Outgoing SMTP and LMTP over TCP/IP + 48.2. Errors in outgoing SMTP + 48.3. Incoming SMTP messages over TCP/IP + 48.4. Unrecognized SMTP commands + 48.5. Syntax and protocol errors in SMTP commands + 48.6. Use of non-mail SMTP commands + 48.7. The VRFY and EXPN commands + 48.8. The ETRN command + 48.9. Incoming local SMTP + 48.10. Outgoing batched SMTP + 48.11. Incoming batched SMTP + +49. Customizing bounce and warning messages + + 49.1. Customizing bounce messages + 49.2. Customizing warning messages + +50. Some common configuration settings + + 50.1. Sending mail to a smart host + 50.2. Using Exim to handle mailing lists + 50.3. Syntax errors in mailing lists + 50.4. Re-expansion of mailing lists + 50.5. Closed mailing lists + 50.6. Variable Envelope Return Paths (VERP) + 50.7. Virtual domains + 50.8. Multiple user mailboxes + 50.9. Simplified vacation processing + 50.10. Taking copies of mail + 50.11. Intermittently connected hosts + 50.12. Exim on the upstream server host + 50.13. Exim on the intermittently connected client host + +51. Using Exim as a non-queueing client +52. Log files + + 52.1. Where the logs are written + 52.2. Logging to local files that are periodically "cycled" + 52.3. Datestamped log files + 52.4. Logging to syslog + 52.5. Log line flags + 52.6. Logging message reception + 52.7. Logging deliveries + 52.8. Discarded deliveries + 52.9. Deferred deliveries + 52.10. Delivery failures + 52.11. Fake deliveries + 52.12. Completion + 52.13. Summary of Fields in Log Lines + 52.14. Other log entries + 52.15. Reducing or increasing what is logged + 52.16. Message log + +53. Exim utilities + + 53.1. Finding out what Exim processes are doing (exiwhat) + 53.2. Selective queue listing (exiqgrep) + 53.3. Summarizing the queue (exiqsumm) + 53.4. Extracting specific information from the log (exigrep) + 53.5. Selecting messages by various criteria (exipick) + 53.6. Cycling log files (exicyclog) + 53.7. Mail statistics (eximstats) + 53.8. Checking access policy (exim_checkaccess) + 53.9. Making DBM files (exim_dbmbuild) + 53.10. Finding individual retry times (exinext) + 53.11. Hints database maintenance + 53.12. exim_dumpdb + 53.13. exim_tidydb + 53.14. exim_fixdb + 53.15. Mailbox maintenance (exim_lock) + +54. The Exim monitor + + 54.1. Running the monitor + 54.2. The stripcharts + 54.3. Main action buttons + 54.4. The log display + 54.5. The queue display + 54.6. The queue menu + +55. Security considerations + + 55.1. Building a more "hardened" Exim + 55.2. Root privilege + 55.3. Running Exim without privilege + 55.4. Delivering to local files + 55.5. Running local commands + 55.6. Trust in configuration data + 55.7. IPv4 source routing + 55.8. The VRFY, EXPN, and ETRN commands in SMTP + 55.9. Privileged users + 55.10. Spool files + 55.11. Use of argv[0] + 55.12. Use of %f formatting + 55.13. Embedded Exim path + 55.14. Dynamic module directory + 55.15. Use of sprintf() + 55.16. Use of debug_printf() and log_write() + 55.17. Use of strcat() and strcpy() + +56. Format of spool files - 55.1. Format of the -H file + 56.1. Format of the -H file -56. Support for DKIM (DomainKeys Identified Mail) +57. Support for DKIM (DomainKeys Identified Mail) - 56.1. Signing outgoing messages - 56.2. Verifying DKIM signatures in incoming mail + 57.1. Signing outgoing messages + 57.2. Verifying DKIM signatures in incoming mail -57. Adding new drivers or lookup types +58. Adding new drivers or lookup types @@ -657,8 +659,8 @@ 1.1 Exim documentation ---------------------- -This edition of the Exim specification applies to version 4.84 of Exim. -Substantive changes from the 4.83 edition are marked in some renditions of the +This edition of the Exim specification applies to version 4.86 of Exim. +Substantive changes from the 4.85 edition are marked in some renditions of the document; this paragraph is so marked if the rendition is capable of showing a change indicator. @@ -1133,7 +1135,7 @@ remote host. However, the most common places are after each RCPT command, and at the very end of the message. The sysadmin can specify conditions for accepting or rejecting individual recipients or the entire message, - respectively, at these two points (see chapter 42). Denial of access + respectively, at these two points (see chapter 43). Denial of access results in an SMTP error code. * An ACL is also available for locally generated, non-SMTP messages. In this @@ -1147,7 +1149,7 @@ * When a message has been received, either from a remote host or from the local host, but before the final acknowledgment has been sent, a locally supplied C function called local_scan() can be run to inspect the message - and decide whether to accept it or not (see chapter 44). If the message is + and decide whether to accept it or not (see chapter 45). If the message is accepted, the list of recipients can be modified by the function. * Using the local_scan() mechanism is another way of calling external scanner @@ -1155,7 +1157,7 @@ Exim to be compiled with the content-scanning extension. * After a message has been accepted, a further checking mechanism is - available in the form of the system filter (see chapter 45). This runs at + available in the form of the system filter (see chapter 46). This runs at the start of every delivery process. @@ -1269,7 +1271,7 @@ (either over TCP/IP, or interacting with a local process) can be checked by a number of ACLs that operate at different times during the SMTP session. Either individual recipients, or the entire message, can be rejected if local policy -requirements are not met. The local_scan() function (see chapter 44) is run for +requirements are not met. The local_scan() function (see chapter 45) is run for all incoming messages. Exim can be configured not to start a delivery process when a message is @@ -1303,7 +1305,7 @@ the addresses of the recipients. This information is entirely separate from any addresses contained in the header lines. The status of the message includes a list of recipients who have already received the message. The format of the -first spool file is described in chapter 55. +first spool file is described in chapter 56. Address rewriting that is specified in the rewrite section of the configuration (see chapter 31) is done once and for all on incoming addresses, both in the @@ -1337,7 +1339,7 @@ While Exim is working on a message, it writes information about each delivery attempt to its main log file. This includes successful, unsuccessful, and -delayed deliveries for each recipient (see chapter 51). The log lines are also +delayed deliveries for each recipient (see chapter 52). The log lines are also written to a separate message log file for each message. These logs are solely for the benefit of the administrator, and are normally deleted along with the spool files when processing of a message is complete. The use of individual @@ -1607,7 +1609,7 @@ interfaces to mail filtering. (Note: Sieve cannot be used for system filter files.) - Some additional features are available in system filters - see chapter 45 + Some additional features are available in system filters - see chapter 46 for details. Note that a message is passed to the system filter only once per delivery attempt, however many recipients it has. However, if there are several delivery attempts because one or more addresses could not be @@ -1732,7 +1734,7 @@ many recipients, it is possible for some addresses to fail in one delivery attempt and others to fail subsequently, giving rise to more than one bounce message. The wording of bounce messages can be customized by the administrator. -See chapter 48 for details. +See chapter 49 for details. Bounce messages contain an X-Failed-Recipients: header line that lists the failed addresses, for the benefit of programs that try to analyse such messages @@ -1743,7 +1745,7 @@ address given in the MAIL command. However, when an address is expanded via a forward or alias file, an alternative address can be specified for delivery failures of the generated addresses. For a mailing list expansion (see section -49.2) it is common to direct bounce messages to the manager of the list. +50.2) it is common to direct bounce messages to the manager of the list. 3.17 Failures to deliver bounce messages @@ -1766,7 +1768,7 @@ Exim is distributed as a gzipped or bzipped tar file which, when unpacked, creates a directory with the name of the current release (for example, -exim-4.84) into which the following files are placed: +exim-4.86) into which the following files are placed: ACKNOWLEDGMENTS contains some acknowledgments CHANGES contains a reference to where changes are documented @@ -1940,7 +1942,7 @@ WITH_CONTENT_SCAN=yes in your Local/Makefile. For details of the facilities themselves, see chapter -43. +44. If you are going to build the Exim monitor, a similar configuration process is required. The file exim_monitor/EDITME must be edited appropriately for your @@ -2029,7 +2031,7 @@ You do not need to set TLS_INCLUDE if the relevant directory is already specified in INCLUDE. Details of how to configure Exim to make use of TLS are -given in chapter 41. +given in chapter 42. 4.8 Use of tcpwrappers @@ -2073,10 +2075,10 @@ defined. AAAA records (analogous to A records for IPv4) are in use, and are currently seen as the mainstream. Another record type called A6 was proposed as better than AAAA because it had more flexibility. However, it was felt to be -over-complex, and its status was reduced to "experimental". It is not known if -anyone is actually using A6 records. Exim has support for A6 records, but this -is included only if you set "SUPPORT_A6=YES" in Local/Makefile. The support has -not been tested for some time. +over-complex, and its status was reduced to "experimental". + +Exim used to have a compile option for including A6 record support but this has +now been withdrawn. 4.10 Dynamically loaded lookup module support @@ -2327,7 +2329,7 @@ for normal configurations. Therefore, you must run "make install" as root so that it can set up the Exim binary in this way. However, in some special situations (for example, if a host is doing no local deliveries) it may be -possible to run Exim without making the binary setuid root (see chapter 54 for +possible to run Exim without making the binary setuid root (see chapter 55 for details). Exim's run time configuration file is named by the CONFIGURE_FILE setting in @@ -2376,7 +2378,7 @@ For the utility programs, old versions are renamed by adding the suffix .O to their names. The Exim binary itself, however, is handled differently. It is installed under a name that includes the version number and the compile number, -for example exim-4.84-1. The script then arranges for a symbolic link called +for example exim-4.86-1. The script then arranges for a symbolic link called exim to point to the binary. If you are updating a previous version of Exim, the script takes care to ensure that the name exim is never absent from the directory (as seen by other processes). @@ -2885,7 +2887,7 @@ actually perform an ident callout when testing using -bh because there is no incoming SMTP connection. - Warning 2: Address verification callouts (see section 42.45) are also + Warning 2: Address verification callouts (see section 43.45) are also skipped when testing using -bh. If you want these callouts to occur, use -bhc instead. @@ -2898,7 +2900,7 @@ The exim_checkaccess utility is a "packaged" version of -bh whose output just states whether a given recipient address from a given host is - acceptable or not. See section 52.8. + acceptable or not. See section 53.8. Features such as authentication and encryption, where the client input is not plain text, cannot easily be tested with -bh. Instead, you should use a @@ -2963,7 +2965,7 @@ this for special cases. Policy checks on the contents of local messages can be enforced by means of - the non-SMTP ACL. See chapter 42 for details. + the non-SMTP ACL. See chapter 43 for details. The return code is zero if the message is successfully accepted. Otherwise, the action is controlled by the -oex option setting - see below. @@ -3189,7 +3191,7 @@ follow. As for other local message submissions, the contents of incoming batch SMTP - messages can be checked using the non-SMTP ACL (see chapter 42). + messages can be checked using the non-SMTP ACL (see chapter 43). Unqualified addresses are automatically qualified using qualify_domain and qualify_recipient, as appropriate, unless the -bnq option is used. @@ -3202,13 +3204,13 @@ error was detected; it is 1 if one or more messages were accepted before the error was detected; otherwise it is 2. - More details of input using batched SMTP are given in section 47.11. + More details of input using batched SMTP are given in section 48.11. -bs This option causes Exim to accept one or more messages by reading SMTP commands on the standard input, and producing SMTP replies on the standard - output. SMTP policy controls, as defined in ACLs (see chapter 42) are + output. SMTP policy controls, as defined in ACLs (see chapter 43) are applied. Some user agents use this interface as a way of passing locally-generated messages to the MTA. @@ -3289,7 +3291,7 @@ is taken as a recipient address to be verified by the routers. (This does not involve any verification callouts). During normal operation, verification happens mostly as a consequence processing a verify condition - in an ACL (see chapter 42). If you want to test an entire ACL, possibly + in an ACL (see chapter 43). If you want to test an entire ACL, possibly including callouts, see the -bh and -bhc options. If verification fails, and the caller is not an admin user, no details of @@ -3461,7 +3463,7 @@ interface lists of local interfaces lists matching things in lists load system load checks - local_scan can be used by local_scan() (see chapter 44) + local_scan can be used by local_scan() (see chapter 45) lookup general lookup code and all lookups memory memory handling pid add pid to debug output lines @@ -3516,7 +3518,7 @@ This is an obsolete option that is now a no-op. It used to affect the way Exim handled CR and LF characters in incoming messages. What happens now is - described in section 46.2. + described in section 47.2. -E @@ -3648,7 +3650,7 @@ This option is not intended for use by external callers. It is used internally by Exim to invoke another instance of itself to deliver a waiting message using an existing SMTP connection, which is passed as the - standard input. Details are given in chapter 47. This must be the final + standard input. Details are given in chapter 48. This must be the final option, and the caller must be root or the Exim user in order to use it. -MCA @@ -3657,6 +3659,12 @@ internally by Exim in conjunction with the -MC option. It signifies that the connection to the remote host has been authenticated. +-MCD + + This option is not intended for use by external callers. It is used + internally by Exim in conjunction with the -MC option. It signifies that + the remote host supports the ESMTP DSN extension. + -MCP This option is not intended for use by external callers. It is used @@ -3691,7 +3699,7 @@ turn, but unlike the -M option, it does check for retry hints, and respects any that are found. This option is not very useful to external callers. It is provided mainly for internal use by Exim when it needs to re-invoke - itself in order to regain root privilege for a delivery (see chapter 54). + itself in order to regain root privilege for a delivery (see chapter 55). However, -Mc can be useful when testing, in order to run a delivery that respects retry times and other options such as hold_domains that are overridden when -M is used. Such a delivery does not count as a queue run. @@ -3871,7 +3879,7 @@ If there is a temporary delivery error during foreground delivery, the message is left on the queue for later delivery, and the original reception - process exits. See chapter 50 for a way of setting up a restricted + process exits. See chapter 51 for a way of setting up a restricted configuration that never queues messages. -odi @@ -4279,7 +4287,7 @@ The -R option makes it straightforward to initiate delivery of all messages to a given domain after a host has been down for some time. When the SMTP - command ETRN is accepted by its ACL (see chapter 42), its default effect is + command ETRN is accepted by its ACL (see chapter 43), its default effect is to run Exim with the -R option, but it can be configured to run an arbitrary command instead. @@ -4346,7 +4354,7 @@ This option is available when Exim is compiled with TLS support. It forces all incoming SMTP connections to behave as if the incoming port is listed - in the tls_on_connect_ports option. See section 13.4 and chapter 41 for + in the tls_on_connect_ports option. See section 13.4 and chapter 42 for further details. -U @@ -4378,6 +4386,12 @@ This option is interpreted by Sendmail to cause debug information to be sent to the named file. It is ignored by Exim. +-z + + This option writes its argument to Exim's logfile. Use is restricted to + administrators; the intent is for operational notes. Quotes should be used + to maintain a multi-word item as a single argument, under most shells. + =============================================================================== @@ -4493,7 +4507,7 @@ optional parts are: * ACL: Access control lists for controlling incoming SMTP mail (see chapter - 42). + 43). * authenticators: Configuration settings for the authenticator drivers. These are concerned with the SMTP AUTH command (see chapter 33). @@ -4522,7 +4536,7 @@ LOCAL_SCAN_HAS_OPTIONS=yes in Local/Makefile before building Exim. Details of the local_scan() - facility are given in chapter 44. + facility are given in chapter 45. Leading and trailing white space in configuration lines is always ignored. @@ -4542,7 +4556,7 @@ as required. The ACLs, retry rules, and rewriting rules have their own syntax which is -described in chapters 42, 32, and 31, respectively. The other parts of the +described in chapters 43, 32, and 31, respectively. The other parts of the configuration file have some syntactic items in common, and these are described below, from section 6.10 onwards. Before that, the inclusion, macro, and conditional facilities are described. @@ -4693,10 +4707,10 @@ message_size_limit = 100M .endif -sets a message size limit of 50M if the macro "AAA" is defined, and 100M -otherwise. If there is more than one macro named on the line, the condition is -true if any of them are defined. That is, it is an "or" condition. To obtain an -"and" condition, you need to use nested ".ifdef"s. +sets a message size limit of 50M if the macro "AAA" is defined (or "A" or +"AA"), and 100M otherwise. If there is more than one macro named on the line, +the condition is true if any of them are defined. That is, it is an "or" +condition. To obtain an "and" condition, you need to use nested ".ifdef"s. Although you can use a macro expansion to generate one of these directives, it is not very useful, because the condition "there was a macro substitution in @@ -5124,7 +5138,7 @@ These are example settings that can be used when Exim is compiled with the content-scanning extension. The first specifies the interface to the virus scanner, and the second specifies the interface to SpamAssassin. Further -details are given in chapter 43. +details are given in chapter 44. Three more commented-out option settings follow: @@ -5138,7 +5152,7 @@ case the wildcard means all clients. The other options specify where Exim should find its TLS certificate and private key, which together prove the server's identity to any clients that connect. More details are given in -chapter 41. +chapter 42. Another two commented-out option settings follow: @@ -5216,7 +5230,7 @@ If you have hosts for which you trust RFC1413 and need this information, you can change this. -This line enables an efficiency SMTP option. It is negociated by clients and +This line enables an efficiency SMTP option. It is negotiated by clients and not expected to cause problems but can be disabled if needed. prdr_enable = true @@ -5232,6 +5246,12 @@ show how you can specify hosts that are permitted to send unqualified sender and recipient addresses, respectively. +The log_selector option is used to increase the detail of logging over the +default: + +log_selector = +smtp_protocol_error +smtp_syntax_error \ + +tls_certificate_verified + The percent_hack_domains option is also commented out: # percent_hack_domains = @@ -5369,7 +5389,7 @@ address is refused. Verification consists of trying to route the address, to see if a bounce message could be delivered to it. In the case of remote addresses, basic verification checks only the domain, but callouts can be used -for more verification if required. Section 42.44 discusses the details of +for more verification if required. Section 43.44 discusses the details of address verification. accept hosts = +relay_from_hosts @@ -5380,7 +5400,7 @@ verification is omitted here, because in many cases the clients are dumb MUAs that do not cope well with SMTP error responses. For the same reason, the second line specifies "submission mode" for messages that are accepted. This is -described in detail in section 46.1; it causes Exim to fix messages that are +described in detail in section 47.1; it causes Exim to fix messages that are deficient in some way, for example, because they lack a Date: header line. If you are actually relaying out from MTAs, you should probably add recipient verification here, and disable submission mode. @@ -5984,14 +6004,14 @@ incoming SMTP calls using the passwords from Courier's /etc/ userdbshadow.dat file. Exim's utility program for creating DBM files ( exim_dbmbuild) includes the zeros by default, but has an option to omit - them (see section 52.9). + them (see section 53.9). * dsearch: The given file must be a directory; this is searched for an entry whose name is the key by calling the lstat() function. The key may not contain any forward slash characters. If lstat() succeeds, the result of the lookup is the name of the entry, which may be a file, directory, symbolic link, or any other kind of directory entry. An example of how this - lookup can be used to support virtual domains is given in section 49.7. + lookup can be used to support virtual domains is given in section 50.7. * iplsearch: The given file is a text file containing keys and data. A key is terminated by a colon or white space or the end of the line. The keys in @@ -6130,16 +6150,16 @@ returns attributes from a single entry. There is a variant called ldapm that permits values from multiple entries to be returned. A third variant called ldapdn returns the Distinguished Name of a single entry instead of - any attribute values. See section 9.13. + any attribute values. See section 9.14. * mysql: The format of the query is an SQL statement that is passed to a - MySQL database. See section 9.20. + MySQL database. See section 9.21. * nisplus: This does a NIS+ lookup using a query that can specify the name of - the field to be returned. See section 9.19. + the field to be returned. See section 9.20. * oracle: The format of the query is an SQL statement that is passed to an - Oracle database. See section 9.20. + Oracle database. See section 9.21. * passwd is a query-style lookup with queries that are just user names. The lookup calls getpwnam() to interrogate the system password data, and on @@ -6150,10 +6170,10 @@ *:42:42:King Rat:/home/kr:/bin/bash * pgsql: The format of the query is an SQL statement that is passed to a - PostgreSQL database. See section 9.20. + PostgreSQL database. See section 9.21. * sqlite: The format of the query is a file name followed by an SQL statement - that is passed to an SQLite database. See section 9.25. + that is passed to an SQLite database. See section 9.26. * testdb: This is a lookup type that is used for testing Exim. It is not likely to be useful in normal operation. @@ -6384,23 +6404,11 @@ keyword causes a forced expansion failure - see section 11.4 for an explanation of what this means. -The supported DNS record types are A, CNAME, MX, NS, PTR, SPF, SRV, TLSA and -TXT, and, when Exim is compiled with IPv6 support, AAAA (and A6 if that is also -configured). If no type is given, TXT is assumed. When the type is PTR, the -data can be an IP address, written as normal; inversion and the addition of -in-addr.arpa or ip6.arpa happens automatically. For example: - -${lookup dnsdb{ptr=192.168.4.5}{$value}fail} - -If the data for a PTR record is not a syntactically valid IP address, it is not -altered and nothing is added. - -For an MX lookup, both the preference value and the host name are returned for -each record, separated by a space. For an SRV lookup, the priority, weight, -port, and host name are returned for each record, separated by spaces. +The supported DNS record types are A, CNAME, MX, NS, PTR, SOA, SPF, SRV, TLSA +and TXT, and, when Exim is compiled with IPv6 support, AAAA. If no type is +given, TXT is assumed. -For any record type, if multiple records are found (or, for A6 lookups, if a -single record leads to multiple addresses), the data is returned as a +For any record type, if multiple records are found, the data is returned as a concatenation, with newline as the default separator. The order, of course, depends on the DNS resolver. You can specify a different separator character between multiple records by putting a right angle-bracket followed immediately @@ -6409,13 +6417,28 @@ ${lookup dnsdb{>: a=host1.example}} It is permitted to specify a space as the separator character. Further white -space is ignored. +space is ignored. For lookup types that return multiple fields per record, an +alternate field separator can be specified using a comma after the main +separator character, followed immediately by the field separator. + +When the type is PTR, the data can be an IP address, written as normal; +inversion and the addition of in-addr.arpa or ip6.arpa happens automatically. +For example: + +${lookup dnsdb{ptr=192.168.4.5}{$value}fail} + +If the data for a PTR record is not a syntactically valid IP address, it is not +altered and nothing is added. + +For an MX lookup, both the preference value and the host name are returned for +each record, separated by a space. For an SRV lookup, the priority, weight, +port, and host name are returned for each record, separated by spaces. The +field separator can be modified as above. For TXT records with multiple items of data, only the first item is returned, -unless a separator for them is specified using a comma after the separator -character followed immediately by the TXT record item separator. To concatenate -items without a separator, use a semicolon instead. For SPF records the default -behaviour is to concatenate multiple items without using a separator. +unless a field separator is specified. To concatenate items without a +separator, use a semicolon instead. For SPF records the default behaviour is to +concatenate multiple items without using a separator. ${lookup dnsdb{>\n,: txt=a.b.example}} ${lookup dnsdb{>\n; txt=a.b.example}} @@ -6424,8 +6447,53 @@ It is permitted to specify a space as the separator character. Further white space is ignored. +For an SOA lookup, while no result is obtained the lookup is redone with +successively more leading components dropped from the given domain. Only the +primary-nameserver field is returned unless a field separator is specified. + +${lookup dnsdb{>:,; soa=a.b.example.com}} + -9.11 Pseudo dnsdb record types +9.11 Dnsdb lookup modifiers +--------------------------- + +Modifiers for dnsdb lookups are givien by optional keywords, each followed by a +comma, that may appear before the record type. + +The dnsdb lookup fails only if all the DNS lookups fail. If there is a +temporary DNS error for any of them, the behaviour is controlled by a +defer-option modifier. The possible keywords are "defer_strict", "defer_never", +and "defer_lax". With "strict" behaviour, any temporary DNS error causes the +whole lookup to defer. With "never" behaviour, a temporary DNS error is +ignored, and the behaviour is as if the DNS lookup failed to find anything. +With "lax" behaviour, all the queries are attempted, but a temporary DNS error +causes the whole lookup to defer only if none of the other lookups succeed. The +default is "lax", so the following lookups are equivalent: + +${lookup dnsdb{defer_lax,a=one.host.com:two.host.com}} +${lookup dnsdb{a=one.host.com:two.host.com}} + +Thus, in the default case, as long as at least one of the DNS lookups yields +some data, the lookup succeeds. + +Use of DNSSEC is controlled by a dnssec modifier. The possible keywords are +"dnssec_strict", "dnssec_lax", and "dnssec_never". With "strict" or "lax" +DNSSEC information is requested with the lookup. With "strict" a response from +the DNS resolver that is not labelled as authenticated data is treated as +equivalent to a temporary DNS error. The default is "never". + +See also the $lookup_dnssec_authenticated variable. + +Timeout for the dnsdb lookup can be controlled by a retrans modifier. The form +is "retrans_VAL" where VAL is an Exim time specification (eg "5s"). The default +value is set by the main configuration option dns_retrans. + +Retries for the dnsdb lookup can be controlled by a retry modifier. The form if +"retry_VAL" where VAL is an integer. The default count is set by the main +configuration option dns_retry. + + +9.12 Pseudo dnsdb record types ------------------------------ By default, both the preference value and the host name are returned for each @@ -6460,7 +6528,7 @@ list. A third pseudo-type is CSA (Client SMTP Authorization). This looks up SRV -records according to the CSA rules, which are described in section 42.50. +records according to the CSA rules, which are described in section 43.50. Although dnsdb supports SRV lookups directly, this is not sufficient because of the extra parent domain search behaviour of CSA. The result of a successful lookup such as: @@ -6471,14 +6539,14 @@ The authorization code can be "Y" for yes, "N" for no, "X" for explicit authorization required but absent, or "?" for unknown. -The pseudo-type A+ performs an A6 lookup (if configured) followed by an AAAA -and then an A lookup. All results are returned; defer processing (see below) is -handled separately for each lookup. Example: +The pseudo-type A+ performs an AAAA and then an A lookup. All results are +returned; defer processing (see below) is handled separately for each lookup. +Example: ${lookup dnsdb {>; a+=$sender_helo_name}} -9.12 Multiple dnsdb lookups +9.13 Multiple dnsdb lookups --------------------------- In the previous sections, dnsdb lookups for a single domain are described. @@ -6499,35 +6567,8 @@ in the same way that multiple DNS records for a single item are handled. A different separator can be specified, as described above. -Modifiers for dnsdb lookups are givien by optional keywords, each followed by a -comma, that may appear before the record type. -The dnsdb lookup fails only if all the DNS lookups fail. If there is a -temporary DNS error for any of them, the behaviour is controlled by a -defer-option modifier. The possible keywords are "defer_strict", "defer_never", -and "defer_lax". With "strict" behaviour, any temporary DNS error causes the -whole lookup to defer. With "never" behaviour, a temporary DNS error is -ignored, and the behaviour is as if the DNS lookup failed to find anything. -With "lax" behaviour, all the queries are attempted, but a temporary DNS error -causes the whole lookup to defer only if none of the other lookups succeed. The -default is "lax", so the following lookups are equivalent: - -${lookup dnsdb{defer_lax,a=one.host.com:two.host.com}} -${lookup dnsdb{a=one.host.com:two.host.com}} - -Thus, in the default case, as long as at least one of the DNS lookups yields -some data, the lookup succeeds. - -Use of DNSSEC is controlled by a dnssec modifier. The possible keywords are -"dnssec_strict", "dnssec_lax", and "dnssec_never". With "strict" or "lax" -DNSSEC information is requested with the lookup. With "strict" a response from -the DNS resolver that is not labelled as authenticated data is treated as -equivalent to a temporary DNS error. The default is "never". - -See also the $lookup_dnssec_authenticated variable. - - -9.13 More about LDAP +9.14 More about LDAP -------------------- The original LDAP implementation came from the University of Michigan; this has @@ -6566,7 +6607,7 @@ explain how LDAP queries are coded. -9.14 Format of LDAP queries +9.15 Format of LDAP queries --------------------------- An LDAP query takes the form of a URL as defined in RFC 2255. For example, in @@ -6594,7 +6635,7 @@ optional, only taking effect if not specifically set in exim.conf. -9.15 LDAP quoting +9.16 LDAP quoting ----------------- Two levels of quoting are required in LDAP queries, the first for LDAP itself @@ -6652,7 +6693,7 @@ authentication below. -9.16 LDAP connections +9.17 LDAP connections --------------------- The connection to an LDAP server may either be over TCP/IP, or, when OpenLDAP @@ -6722,7 +6763,7 @@ ldap_default_servers, does whatever the library does by default. -9.17 LDAP authentication and control information +9.18 LDAP authentication and control information ------------------------------------------------ The LDAP URL syntax provides no way of passing authentication and other control @@ -6765,7 +6806,7 @@ server to use. But when you need to do a lookup with a list of servers that is different than the default list (maybe different order, maybe a completely different set of servers), the SERVERS parameter allows you to specify this -alternate list. +alternate list (colon-separated). Here is an example of an LDAP query in an Exim lookup that uses some of these values. This is a single line, folded to fit on the page: @@ -6811,7 +6852,7 @@ SMTP authentication. See the ldapauth expansion string condition in chapter 11. -9.18 Format of data returned by LDAP +9.19 Format of data returned by LDAP ------------------------------------ The ldapdn lookup type returns the Distinguished Name from a single entry as a @@ -6834,33 +6875,39 @@ strings, each preceded by the attribute name and an equals sign. Within the quotes, the quote character, backslash, and newline are escaped with backslashes, and commas are used to separate multiple values for the attribute. -Apart from the escaping, the string within quotes takes the same form as the -output when a single attribute is requested. Specifying no attributes is the -same as specifying all of an entry's attributes. +Any commas in attribute values are doubled (permitting treatment of the values +as a comma-separated list). Apart from the escaping, the string within quotes +takes the same form as the output when a single attribute is requested. +Specifying no attributes is the same as specifying all of an entry's +attributes. Here are some examples of the output format. The first line of each pair is an LDAP query, and the second is the data that is returned. The attribute called -attr1 has two values, whereas attr2 has only one value: +attr1 has two values, one of them with an embedded comma, whereas attr2 has +only one value: ldap:///o=base?attr1?sub?(uid=fred) -value1.1, value1.2 +value1.1,value1,,2 ldap:///o=base?attr2?sub?(uid=fred) value two ldap:///o=base?attr1,attr2?sub?(uid=fred) -attr1="value1.1, value1.2" attr2="value two" +attr1="value1.1,value1,,2" attr2="value two" ldap:///o=base??sub?(uid=fred) -objectClass="top" attr1="value1.1, value1.2" attr2="value two" +objectClass="top" attr1="value1.1,value1,,2" attr2="value two" -The extract operator in string expansions can be used to pick out individual -fields from data that consists of key=value pairs. You can make use of Exim's --be option to run expansion tests and thereby check the results of LDAP -lookups. +You can make use of Exim's -be option to run expansion tests and thereby check +the results of LDAP lookups. The extract operator in string expansions can be +used to pick out individual fields from data that consists of key=value pairs. +The listextract operator should be used to pick out individual values of +attributes, even when only a single value is expected. The doubling of embedded +commas allows you to use the returned data as a comma separated list (using the +"<," syntax for changing the input list separator). -9.19 More about NIS+ +9.20 More about NIS+ -------------------- NIS+ queries consist of a NIS+ indexed name followed by an optional colon and @@ -6889,7 +6936,7 @@ is to double any quote characters within the text. -9.20 SQL lookups +9.21 SQL lookups ---------------- Exim can support lookups in InterBase, MySQL, Oracle, PostgreSQL, and SQLite @@ -6919,14 +6966,14 @@ with a newline between the data for each row. -9.21 More about MySQL, PostgreSQL, Oracle, and InterBase +9.22 More about MySQL, PostgreSQL, Oracle, and InterBase -------------------------------------------------------- If any MySQL, PostgreSQL, Oracle, or InterBase lookups are used, the mysql_servers, pgsql_servers, oracle_servers, or ibase_servers option (as appropriate) must be set to a colon-separated list of server information. (For MySQL and PostgreSQL only, the global option need not be set if all queries -contain their own server information - see section 9.22.) Each item in the list +contain their own server information - see section 9.23.) Each item in the list is a slash-separated list of four items: host name, database name, user name, and password. In the case of Oracle, the host name field is used for the "service name", and the database name field is not used and should be empty. @@ -6957,7 +7004,7 @@ characters are not special. -9.22 Specifying the server in the query +9.23 Specifying the server in the query --------------------------------------- For MySQL and PostgreSQL lookups (but not currently for Oracle and InterBase), @@ -6999,7 +7046,7 @@ ${lookup pgsql{servers=master/db/name/pw; UPDATE ...} } -9.23 Special MySQL features +9.24 Special MySQL features --------------------------- For MySQL, an empty host name or the use of "localhost" in mysql_servers causes @@ -7023,7 +7070,7 @@ because no rows are affected. -9.24 Special PostgreSQL features +9.25 Special PostgreSQL features -------------------------------- PostgreSQL lookups can also use Unix domain socket connections to the database. @@ -7043,7 +7090,7 @@ affected. -9.25 More about SQLite +9.26 More about SQLite ---------------------- SQLite is different to the other SQL lookups because a file name is required in @@ -7079,7 +7126,7 @@ A number of Exim configuration options contain lists of domains, hosts, email addresses, or local parts. For example, the hold_domains option contains a list of domains whose delivery is currently suspended. These lists are also used as -data in ACL statements (see chapter 42), and as arguments to expansion +data in ACL statements (see chapter 43), and as arguments to expansion conditions such as match_domain. Each item in one of these lists is a pattern to be matched against a domain, @@ -7087,6 +7134,9 @@ different types of pattern for each case are described, but first we cover some general facilities that apply to all four kinds of list. +Note that other parts of Exim use a string list which does not support all the +complexity availible in domain, host, address and local part lists. + 10.1 Expansion of lists ----------------------- @@ -7778,7 +7828,7 @@ accept hosts = 10.9.8.7 If the first accept fails, Exim goes on to try the second one. See chapter - 42 for details of ACLs. Alternatively, you can use "+ignore_unknown", which + 43 for details of ACLs. Alternatively, you can use "+ignore_unknown", which was discussed in depth in the first example in this section. @@ -8218,7 +8268,7 @@ The name and zero to nine argument strings are first expanded separately. The expanded arguments are assigned to the variables $acl_arg1 to $acl_arg9 in order. Any unused are made empty. The variable $acl_narg is set to the - number of arguments. The named ACL (see chapter 42) is called and may use + number of arguments. The named ACL (see chapter 43) is called and may use the variables; if another acl expansion is used the values are restored after it returns. If the ACL sets a value using a "message =" modifier and returns accept or deny, the value becomes the result of the expansion. If @@ -8257,15 +8307,17 @@ The field selectors marked as "RFC4514" above output a Distinguished Name string which is not quite parseable by Exim as a comma-separated tagged - list (the exceptions being elements containin commas). RDN elements of a + list (the exceptions being elements containing commas). RDN elements of a single type may be selected by a modifier of the type label; if so the expansion result is a list (newline-separated by default). The separator may be changed by another modifer of a right angle-bracket followed immediately by the new separator. Recognised RDN type labels include "CN", "O", "OU" and "DC". - The field selectors marked as "time" above may output a number of seconds - since epoch if the modifier "int" is used. + The field selectors marked as "time" above take an optional modifier of + "int" for which the result is the number of seconds since epoch. Otherwise + the result is a human-readable string in the timezone selected by the main + "timezone" option. The field selectors marked as "list" above return a list, newline-separated by default, (embedded separator characters in elements are doubled). The @@ -8316,6 +8368,25 @@ to add -shared to the gcc command. Also, in the Exim build-time configuration, you must add -export-dynamic to EXTRALIBS. +${env{}{}{}} + + The key is first expanded separately, and leading and trailing white space + removed. This is then searched for as a name in the environment. If a + variable is found then its value is placed in $value and is + expanded, otherwise is expanded. + + Instead of {} the word "fail" (not in curly brackets) can appear, + for example: + + ${env{USER}{$value} fail } + + This forces an expansion failure (see section 11.4); {} must be + present for "fail" to be recognized. + + If {} is omitted an empty string is substituted on search failure. + If {} is omitted the search result is substituted on search + success. + ${extract{}{}{}{}} The key and are first expanded separately. Leading and trailing @@ -8728,7 +8799,7 @@ absent, it defaults to 0. The result of the expansion is a prvs-signed email address, to be typically used with the return_path option on an smtp transport as part of a bounce address tag validation (BATV) scheme. For - more discussion and an example, see section 42.51. + more discussion and an example, see section 43.51. ${prvscheck{
}{}{}} @@ -8754,7 +8825,7 @@ All three variables can be used in the expansion of the third argument. However, once the expansion is complete, only $prvscheck_result remains - set. For more discussion and an example, see section 42.51. + set. For more discussion and an example, see section 43.51. ${readfile{}{}} @@ -8771,9 +8842,9 @@ ${readsocket{}{}{}{}{}} - This item inserts data from a Unix domain or Internet socket into the - expanded string. The minimal way of using it uses just two arguments, as in - these examples: + This item inserts data from a Unix domain or TCP socket into the expanded + string. The minimal way of using it uses just two arguments, as in these + examples: ${readsocket{/socket/name}{request string}} ${readsocket{inet:some.host:1234}{request string}} @@ -8948,6 +9019,30 @@ yields "K1=A K4=D K3=C". Note the use of "\N" to protect the contents of the regular expression from string expansion. +${sort{}{}{}} + + After expansion, is interpreted as a list, colon-separated by + default, but the separator can be changed in the usual way. The argument is interpreted as the operator of a two-argument expansion + condition. The numeric operators plus ge, gt, le, lt (and ~i variants) are + supported. The comparison should return true when applied to two values if + the first value should sort before the second value. The + expansion is applied repeatedly to elements of the list, the element being + placed in $item, to give values for comparison. + + The item result is a sorted list, with the original list separator, of the + list elements (in full) of the original. + + Examples: + + ${sort{3:2:1:4}{<}{$item}} + + sorts a list of numbers, and + + ${sort {${lookup dnsdb{>:,,mx=example.com}}} {<} {${listextract{1}{<,$item}}}} + + will sort an MX lookup into priority order. + ${substr{}{}{}} The three strings are expanded; the first two must yield numbers. Call them @@ -9497,7 +9592,7 @@ The name and zero to nine argument strings are first expanded separately. The expanded arguments are assigned to the variables $acl_arg1 to $acl_arg9 in order. Any unused are made empty. The variable $acl_narg is set to the - number of arguments. The named ACL (see chapter 42) is called and may use + number of arguments. The named ACL (see chapter 43) is called and may use the variables; if another acl expansion is used the values are restored after it returns. If the ACL sets a value using a "message =" modifier the variable $value becomes the result of the expansion, otherwise it is empty. @@ -9723,7 +9818,7 @@ ldapauth {} - This condition supports user authentication using LDAP. See section 9.13 + This condition supports user authentication using LDAP. See section 9.14 for details of how to use LDAP in lookups and the syntax of queries. For this use, the query must contain a user name and password. The query itself is not used, and can be empty. The condition is true if the password is not @@ -10212,13 +10307,13 @@ This is set to the recipient address of a bounce message while Exim is creating it. It is useful if a customized bounce message text file is in - use (see chapter 48). + use (see chapter 49). $bounce_return_size_limit This contains the value set in the bounce_return_size_limit option, rounded up to a multiple of 1000. It is useful when a customized error message text - file is in use (see chapter 48). + file is in use (see chapter 49). $caller_gid @@ -10244,22 +10339,48 @@ been compiled. This serves to distinguish different compilations of the same version of the program. +$config_dir + + The directory name of the main configuration file. That is, the content of + $config_file with the last component stripped. The value does not contain + the trailing slash. If $config_file does not contain a slash, $config_dir + is ".". + +$config_file + + The name of the main configuration file Exim is using. + $demime_errorlevel This variable is available when Exim is compiled with the content-scanning - extension and the obsolete demime condition. For details, see section 43.6. + extension and the obsolete demime condition. For details, see section 44.6. $demime_reason This variable is available when Exim is compiled with the content-scanning - extension and the obsolete demime condition. For details, see section 43.6. + extension and the obsolete demime condition. For details, see section 44.6. + +$dkim_cur_signer, $dkim_verify_status, $dkim_verify_reason, $dkim_domain, + $dkim_identity, $dkim_selector, $dkim_algo, $dkim_canon_body, + $dkim_canon_headers, $dkim_copiedheaders, $dkim_bodylength, $dkim_created, + $dkim_expires, $dkim_headernames, $dkim_key_testing, $dkim_key_nosubdomains + , $dkim_key_srvtype, $dkim_key_granularity, $dkim_key_notes + + These variables are only available within the DKIM ACL. For details see + chapter 57. + +$dkim_signers + + When a message has been received this variable contains a colon-separated + list of signer domains and identities for the message. For details see + chapter 57. $dnslist_domain, $dnslist_matched, $dnslist_text, $dnslist_value When a DNS (black) list lookup succeeds, these variables are set to contain the following data from the lookup: the list's domain name, the key that was looked up, the contents of any associated TXT record, and the value - from the main A record. See section 42.32 for more details. + from the main A record. See section 43.32 for more details. $domain @@ -10306,7 +10427,7 @@ the recipient domain (which is what is in $domain at this time). * When the smtp_etrn_command option is being expanded, $domain contains - the complete argument of the ETRN command (see section 47.8). + the complete argument of the ETRN command (see section 48.8). $domain_data @@ -10333,10 +10454,17 @@ This variable contains the numerical value of the Exim user id. +$exim_version + + This variable contains the version string of the Exim build. The first + character is a major version number, currently 4. Then after a dot, the + next group of digits is a minor version number. There may be other + characters following the minor version. + $found_extension This variable is available when Exim is compiled with the content-scanning - extension and the obsolete demime condition. For details, see section 43.6. + extension and the obsolete demime condition. For details, see section 44.6. $header_ @@ -10348,7 +10476,7 @@ $headers_added Within an ACL this variable contains the headers added so far by the ACL - modifier add_header (section 42.24). The headers are a newline-separated + modifier add_header (section 43.24). The headers are a newline-separated list. $home @@ -10424,6 +10552,11 @@ See $host_lookup_deferred. +$host_port + + This variable is set to the remote host's TCP port whenever $host is set + for an outbound connection. + $inode The only time this variable is set is while expanding the directory_file @@ -10536,7 +10669,7 @@ $local_scan_data This variable contains the text returned by the local_scan() function when - a message is received. See chapter 44 for more details. + a message is received. See chapter 45 for more details. $local_user_gid @@ -10578,6 +10711,9 @@ requested, "no" if the result was not labelled as authenticated data and "yes" if it was. + Results that are labelled as authoritive answer that match the dns_trust_aa + configuration variable count also as authenticated data. + $mailstore_basename This variable is set only when doing deliveries in "mailstore" format in @@ -10591,7 +10727,7 @@ This variable is available when Exim is compiled with the content-scanning extension. It is set to the name of the virus that was found when the ACL - malware condition is true (see section 43.1). + malware condition is true (see section 44.1). $max_received_linelength @@ -10653,7 +10789,7 @@ $message_id - This is an old name for $message_exim_id, which is now deprecated. + This is an old name for $message_exim_id. It is now deprecated. $message_linecount @@ -10700,7 +10836,7 @@ A number of variables whose names start with $mime are available when Exim is compiled with the content-scanning extension. For details, see section - 43.4. + 44.4. $n0 - $n9 @@ -10787,17 +10923,17 @@ $prvscheck_address This variable is used in conjunction with the prvscheck expansion item, - which is described in sections 11.5 and 42.51. + which is described in sections 11.5 and 43.51. $prvscheck_keynum This variable is used in conjunction with the prvscheck expansion item, - which is described in sections 11.5 and 42.51. + which is described in sections 11.5 and 43.51. $prvscheck_result This variable is used in conjunction with the prvscheck expansion item, - which is described in sections 11.5 and 42.51. + which is described in sections 11.5 and 43.51. $qualify_domain @@ -10854,11 +10990,8 @@ certificate depend on which interface and/or port is being used for the incoming connection. The values of $received_ip_address and $received_port are saved with any messages that are received, thus making these variables - available at delivery time. - - Note: There are no equivalent variables for outgoing connections, because - the values are unknown (unless they are explicitly set by options of the - smtp transport). + available at delivery time. For outbound connections see + $sending_ip_address. $received_port @@ -10952,7 +11085,7 @@ $regex_match_string This variable is set to contain the matching regular expression after a - regex ACL condition has matched (see section 43.5). + regex ACL condition has matched (see section 44.5). $reply_address @@ -11055,6 +11188,11 @@ if it is identical to the verified host name or to the host's IP address in square brackets. +$sender_helo_dnssec + + This boolean variable is true if a successful HELO verification was done + using DNS information the resolver library stated was authenticatied data. + $sender_helo_name When a message is received from a remote host that has issued a HELO or @@ -11064,8 +11202,9 @@ $sender_host_address - When a message is received from a remote host, this variable contains that - host's IP address. For locally submitted messages, it is empty. + When a message is received from a remote host using SMTP, this variable + contains that host's IP address. For locally non-SMTP submitted messages, + it is empty. $sender_host_authenticated @@ -11078,8 +11217,8 @@ If an attempt to populate $sender_host_name has been made (by reference, hosts_lookup or otherwise) then this boolean will have been set true if, - and only if, the resolver library states that the reverse DNS was - authenticated data. At all other times, this variable is false. + and only if, the resolver library states that both the reverse and forward + DNS were authenticated data. At all other times, this variable is false. It is likely that you will need to coerce DNSSEC support on in the resolver library, by setting: @@ -11089,9 +11228,6 @@ Exim does not perform DNSSEC validation itself, instead leaving that to a validating resolver (eg, unbound, or bind with suitable configuration). - Exim does not (currently) check to see if the forward DNS was also secured - with DNSSEC, only the reverse DNS. - If you have changed host_lookup_order so that "bydns" is not the first mechanism in the list, then this variable will be false. @@ -11160,7 +11296,7 @@ $sender_rate_xxx A number of variables whose names begin $sender_rate_ are set as part of - the ratelimit ACL condition. Details are given in section 42.38. + the ratelimit ACL condition. Details are given in section 43.38. $sender_rcvhost @@ -11254,7 +11390,7 @@ A number of variables whose names start with $spam are available when Exim is compiled with the content-scanning extension. For details, see section - 43.2. + 44.2. $spool_directory @@ -11309,25 +11445,33 @@ This variable refers to the certificate presented to the peer of an inbound connection when the message was received. It is only useful as the argument - of a certextract expansion item, md5 or sha1 operator, or a def condition. + of a certextract expansion item, md5, sha1 or sha256 operator, or a def + condition. $tls_in_peercert This variable refers to the certificate presented by the peer of an inbound connection when the message was received. It is only useful as the argument - of a certextract expansion item, md5 or sha1 operator, or a def condition. + of a certextract expansion item, md5, sha1 or sha256 operator, or a def + condition. + + If certificate verification fails it may refer to a failing chain element + which is not the leaf. $tls_out_ourcert This variable refers to the certificate presented to the peer of an outbound connection. It is only useful as the argument of a certextract - expansion item, md5 or sha1 operator, or a def condition. + expansion item, md5, sha1 or sha256 operator, or a def condition. $tls_out_peercert This variable refers to the certificate presented by the peer of an outbound connection. It is only useful as the argument of a certextract - expansion item, md5 or sha1 operator, or a def condition. + expansion item, md5, sha1 or sha256 operator, or a def condition. + + If certificate verification fails it may refer to a failing chain element + which is not the leaf. $tls_in_certificate_verified @@ -11359,7 +11503,7 @@ $tls_out_cipher This variable is cleared before any outgoing SMTP connection is made, and - then set to the outgoing cipher suite if one is negotiated. See chapter 41 + then set to the outgoing cipher suite if one is negotiated. See chapter 42 for details of TLS support and chapter 30 for details of the smtp transport. @@ -11386,6 +11530,9 @@ client, the value of the Distinguished Name of the certificate is made available in the $tls_in_peerdn during subsequent processing. + If certificate verification fails it may refer to a failing chain element + which is not the leaf. + The deprecated $tls_peerdn variable refers to the inbound side except when used in the context of an outbound SMTP delivery, when it refers to the outbound. @@ -11397,12 +11544,15 @@ server, the value of the Distinguished Name of the certificate is made available in the $tls_out_peerdn during subsequent processing. + If certificate verification fails it may refer to a failing chain element + which is not the leaf. + $tls_in_sni When a TLS session is being established, if the client sends the Server Name Indication extension, the value will be placed in this variable. If the variable appears in tls_certificate then this option and some others, - described in 41.10, will be re-expanded early in the TLS session, to permit + described in 42.10, will be re-expanded early in the TLS session, to permit a different certificate to be presented (and optionally a different key to be used) to the client, based upon the value of the SNI extension. @@ -11467,6 +11617,12 @@ operation, or external command, as described above. It is also used during a reduce expansion. +$verify_mode + + While a router or transport is being run in verify mode or for cutthrough + delivery, contains "S" for sender-verification or "R" for + recipient-verification. Otherwise, empty. + $version_number The version number of Exim. @@ -11474,12 +11630,12 @@ $warn_message_delay This variable is set only during the creation of a message warning about a - delivery delay. Details of its use are explained in section 48.2. + delivery delay. Details of its use are explained in section 49.2. $warn_message_recipients This variable is set only during the creation of a message warning about a - delivery delay. Details of its use are explained in section 48.2. + delivery delay. Details of its use are explained in section 49.2. @@ -11975,6 +12131,7 @@ message_logs create per-message logs preserve_message_logs after message completion process_log_path for SIGUSR1 and exiwhat +slow_lookup_log control logging of slow DNS lookups syslog_duplication controls duplicate log lines on syslog syslog_facility set syslog "facility" field syslog_processname set syslog "ident" field @@ -12080,6 +12237,7 @@ acl_smtp_mail ACL for MAIL acl_smtp_mailauth ACL for AUTH on MAIL command acl_smtp_mime ACL for MIME parts +acl_smtp_notquit ACL for non-QUIT terminations acl_smtp_predata ACL for start of data acl_smtp_quit ACL for QUIT acl_smtp_rcpt ACL for RCPT @@ -12128,6 +12286,7 @@ tls_crl certificate revocation list tls_dh_max_bits clamp D-H bit count suggestion tls_dhparam DH parameters for server +tls_eccurve EC curve selection for server tls_ocsp_file location of server certificate status proof tls_on_connect_ports specify SSMTP (SMTPS) ports tls_privatekey location of server private key @@ -12175,6 +12334,7 @@ See also the Policy controls section above. +dkim_verify_signers DKIM domain for which DKIM ACL is run host_lookup host name looked up for these hosts host_lookup_order order of DNS and local name lookups recipient_unqualified_hosts may send unqualified recipients @@ -12212,6 +12372,7 @@ accept_8bitmime advertise 8BITMIME auth_advertise_hosts advertise AUTH to these hosts +dsn_advertise_hosts advertise DSN extensions to these hosts ignore_fromline_hosts allow "From " from these hosts ignore_fromline_local allow "From " from local SMTP pipelining_advertise_hosts advertise pipelining to these hosts @@ -12260,6 +12421,7 @@ dns_ipv4_lookup only v4 lookup for these domains dns_retrans parameter for resolver dns_retry parameter for resolver +dns_trust_aa DNS zones trusted as authentic dns_use_edns0 parameter for resolver hold_domains hold delivery for these domains local_interfaces for routing checks @@ -12302,9 +12464,9 @@ Those options that undergo string expansion before use are marked with *. -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ |accept_8bitmime|Use: main|Type: boolean|Default: true| -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ This option causes Exim to send 8BITMIME in its response to an SMTP EHLO command, and to accept the BODY= parameter on MAIL commands. However, though @@ -12322,141 +12484,155 @@ log_selector = +8bitmime -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ |acl_not_smtp|Use: main|Type: string*|Default: unset| -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ This option defines the ACL that is run when a non-SMTP message has been read -and is on the point of being accepted. See chapter 42 for further details. +and is on the point of being accepted. See chapter 43 for further details. -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ |acl_not_smtp_mime|Use: main|Type: string*|Default: unset| -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ This option defines the ACL that is run for individual MIME parts of non-SMTP messages. It operates in exactly the same way as acl_smtp_mime operates for SMTP messages. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |acl_not_smtp_start|Use: main|Type: string*|Default: unset| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ This option defines the ACL that is run before Exim starts reading a non-SMTP -message. See chapter 42 for further details. +message. See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_auth|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP AUTH command is received. -See chapter 42 for further details. +See chapter 43 for further details. -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ |acl_smtp_connect|Use: main|Type: string*|Default: unset| -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ This option defines the ACL that is run when an SMTP connection is received. -See chapter 42 for further details. +See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_data|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run after an SMTP DATA command has been processed and the message itself has been received, but before the final -acknowledgment is sent. See chapter 42 for further details. +acknowledgment is sent. See chapter 43 for further details. -+------------------+---------+-------------+--------------+ -|acl_smtp_data_prdr|Use: main|Type: string*|Default: unset| -+------------------+---------+-------------+--------------+ ++----------------------------------------------------------+ +|acl_smtp_data_prdr|Use: main|Type: string*|Default: accept| ++----------------------------------------------------------+ This option defines the ACL that, if the PRDR feature has been negotiated, is run for each recipient after an SMTP DATA command has been processed and the message itself has been received, but before the acknowledgment is sent. See -chapter 42 for further details. +chapter 43 for further details. + ++----------------------------------------------------+ +|acl_smtp_dkim|Use: main|Type: string*|Default: unset| ++----------------------------------------------------+ -+-------------+---------+-------------+--------------+ +This option defines the ACL that is run for each DKIM signature of a received +message. See chapter 57 for further details. + ++----------------------------------------------------+ |acl_smtp_etrn|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP ETRN command is received. -See chapter 42 for further details. +See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_expn|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP EXPN command is received. -See chapter 42 for further details. +See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_helo|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP EHLO or HELO command is -received. See chapter 42 for further details. +received. See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_mail|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP MAIL command is received. -See chapter 42 for further details. +See chapter 43 for further details. -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ |acl_smtp_mailauth|Use: main|Type: string*|Default: unset| -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ This option defines the ACL that is run when there is an AUTH parameter on a -MAIL command. See chapter 42 for details of ACLs, and chapter 33 for details of +MAIL command. See chapter 43 for details of ACLs, and chapter 33 for details of authentication. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_mime|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option is available when Exim is built with the content-scanning extension. It defines the ACL that is run for each MIME part in a message. See -section 43.4 for details. +section 44.4 for details. + ++-------------------------------------------------------+ +|acl_smtp_notquit|Use: main|Type: string*|Default: unset| ++-------------------------------------------------------+ -+----------------+---------+-------------+--------------+ +This option defines the ACL that is run when an SMTP session ends without a +QUIT command being received. See chapter 43 for further details. + ++-------------------------------------------------------+ |acl_smtp_predata|Use: main|Type: string*|Default: unset| -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ This option defines the ACL that is run when an SMTP DATA command is received, -before the message itself is received. See chapter 42 for further details. +before the message itself is received. See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_quit|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP QUIT command is received. -See chapter 42 for further details. +See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_rcpt|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP RCPT command is received. -See chapter 42 for further details. +See chapter 43 for further details. -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ |acl_smtp_starttls|Use: main|Type: string*|Default: unset| -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ This option defines the ACL that is run when an SMTP STARTTLS command is -received. See chapter 42 for further details. +received. See chapter 43 for further details. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |acl_smtp_vrfy|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option defines the ACL that is run when an SMTP VRFY command is received. -See chapter 42 for further details. +See chapter 43 for further details. -+------------+---------+------------------+--------------+ ++--------------------------------------------------------+ |admin_groups|Use: main|Type: string list*|Default: unset| -+------------+---------+------------------+--------------+ ++--------------------------------------------------------+ This option is expanded just once, at the start of Exim's processing. If the current group or any of the supplementary groups of an Exim caller is in this @@ -12466,9 +12642,9 @@ permit them to read Exim's spool files (whose group owner is the Exim gid). To permit this, you have to add individuals to the Exim group. -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ |allow_domain_literals|Use: main|Type: boolean|Default: false| -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ If this option is set, the RFC 2822 domain literal format is permitted in email addresses. The option is not set by default, because the domain literal format @@ -12482,9 +12658,9 @@ domain list local_domains in the default configuration). This "magic string" matches the domain literal form of all the local host's IP addresses. -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ |allow_mx_to_ip|Use: main|Type: boolean|Default: false| -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ It appears that more and more DNS zone administrators are breaking the rules and putting domain names that look like IP addresses on the right hand side of @@ -12494,9 +12670,9 @@ exists, in order to enable this heinous activity. It is not recommended, except when you have no other choice. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |allow_utf8_domains|Use: main|Type: boolean|Default: false| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ Lots of discussion is going on about internationalized domain names. One camp is strongly in favour of just using UTF-8 characters, and it seems that at @@ -12518,9 +12694,9 @@ That is, set the option to an empty string so that no check is done. -+--------------------+---------+----------------+----------+ ++----------------------------------------------------------+ |auth_advertise_hosts|Use: main|Type: host list*|Default: *| -+--------------------+---------+----------------+----------+ ++----------------------------------------------------------+ If any server authentication mechanisms are configured, Exim advertises them in response to an EHLO command only if the calling host matches this list. @@ -12547,9 +12723,9 @@ expansion is empty, thus matching no hosts. Otherwise, the result of the expansion is *, which matches all hosts. -+---------+---------+----------+-----------+ ++------------------------------------------+ |auto_thaw|Use: main|Type: time|Default: 0s| -+---------+---------+----------+-----------+ ++------------------------------------------+ If this option is set to a time greater than zero, a queue runner will try a new delivery attempt on any frozen message, other than a bounce message, if @@ -12561,9 +12737,9 @@ ignore_bounce_errors_after. It is retained for compatibility, but it is not thought to be very useful any more, and its use should probably be avoided. -+----------+---------+------------+------------------+ ++----------------------------------------------------+ |av_scanner|Use: main|Type: string|Default: see below| -+----------+---------+------------+------------------+ ++----------------------------------------------------+ This option is available if Exim is built with the content-scanning extension. It specifies which anti-virus scanner to use. The default value is: @@ -12571,36 +12747,36 @@ sophie:/var/run/sophie If the value of av_scanner starts with a dollar character, it is expanded -before use. See section 43.1 for further details. +before use. See section 44.1 for further details. -+----------+---------+------------+--------------+ ++------------------------------------------------+ |bi_command|Use: main|Type: string|Default: unset| -+----------+---------+------------+--------------+ ++------------------------------------------------+ This option supplies the name of a command that is run when Exim is called with the -bi option (see chapter 5). The string value is just the command name, it is not a complete command line. If an argument is required, it must come from the -oA command line option. -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ |bounce_message_file|Use: main|Type: string|Default: unset| -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ This option defines a template file containing paragraphs of text to be used for constructing bounce messages. Details of the file's contents are given in -chapter 48. See also warn_message_file. +chapter 49. See also warn_message_file. -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ |bounce_message_text|Use: main|Type: string|Default: unset| -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ When this option is set, its contents are included in the default bounce message immediately after "This message was created automatically by mail delivery software." It is not used if bounce_message_file is set. -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ |bounce_return_body|Use: main|Type: boolean|Default: true| -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ This option controls whether the body of an incoming message is included in a bounce message when bounce_return_message is true. The default setting causes @@ -12610,17 +12786,17 @@ detected during reception, only those header lines preceding the point at which the error was detected are returned. -+---------------------+---------+-------------+-------------+ ++-----------------------------------------------------------+ |bounce_return_message|Use: main|Type: boolean|Default: true| -+---------------------+---------+-------------+-------------+ ++-----------------------------------------------------------+ If this option is set false, none of the original message is included in bounce messages generated by Exim. See also bounce_return_size_limit and bounce_return_body. -+------------------------+---------+-------------+-------------+ ++--------------------------------------------------------------+ |bounce_return_size_limit|Use: main|Type: integer|Default: 100K| -+------------------------+---------+-------------+-------------+ ++--------------------------------------------------------------+ This option sets a limit in bytes on the size of messages that are returned to senders as part of bounce messages when bounce_return_message is true. The @@ -12635,9 +12811,9 @@ size). The idea is to save bandwidth on those undeliverable 15-megabyte messages. -+----------------------------+---------+------------+--------------+ ++------------------------------------------------------------------+ |bounce_sender_authentication|Use: main|Type: string|Default: unset| -+----------------------------+---------+------------+--------------+ ++------------------------------------------------------------------+ This option provides an authenticated sender address that is sent with any bounce messages generated by Exim that are sent over an authenticated SMTP @@ -12652,64 +12828,64 @@ The value of bounce_sender_authentication must always be a complete email address. -+------------------------------+---------+----------+-----------+ ++---------------------------------------------------------------+ |callout_domain_negative_expire|Use: main|Type: time|Default: 3h| -+------------------------------+---------+----------+-----------+ ++---------------------------------------------------------------+ This option specifies the expiry time for negative callout cache data for a -domain. See section 42.45 for details of callout verification, and section -42.47 for details of the caching. +domain. See section 43.45 for details of callout verification, and section +43.47 for details of the caching. -+------------------------------+---------+----------+-----------+ ++---------------------------------------------------------------+ |callout_domain_positive_expire|Use: main|Type: time|Default: 7d| -+------------------------------+---------+----------+-----------+ ++---------------------------------------------------------------+ This option specifies the expiry time for positive callout cache data for a -domain. See section 42.45 for details of callout verification, and section -42.47 for details of the caching. +domain. See section 43.45 for details of callout verification, and section +43.47 for details of the caching. -+-----------------------+---------+----------+-----------+ ++--------------------------------------------------------+ |callout_negative_expire|Use: main|Type: time|Default: 2h| -+-----------------------+---------+----------+-----------+ ++--------------------------------------------------------+ This option specifies the expiry time for negative callout cache data for an -address. See section 42.45 for details of callout verification, and section -42.47 for details of the caching. +address. See section 43.45 for details of callout verification, and section +43.47 for details of the caching. -+-----------------------+---------+----------+------------+ ++---------------------------------------------------------+ |callout_positive_expire|Use: main|Type: time|Default: 24h| -+-----------------------+---------+----------+------------+ ++---------------------------------------------------------+ This option specifies the expiry time for positive callout cache data for an -address. See section 42.45 for details of callout verification, and section -42.47 for details of the caching. +address. See section 43.45 for details of callout verification, and section +43.47 for details of the caching. -+-------------------------+---------+-------------+------------------+ ++--------------------------------------------------------------------+ |callout_random_local_part|Use: main|Type: string*|Default: see below| -+-------------------------+---------+-------------+------------------+ ++--------------------------------------------------------------------+ This option defines the "random" local part that can be used as part of callout verification. The default value is $primary_hostname-$tod_epoch-testing -See section 42.46 for details of how this value is used. +See section 43.46 for details of how this value is used. -+----------------+---------+-------------+----------+ ++---------------------------------------------------+ |check_log_inodes|Use: main|Type: integer|Default: 0| -+----------------+---------+-------------+----------+ ++---------------------------------------------------+ See check_spool_space below. -+---------------+---------+-------------+----------+ ++--------------------------------------------------+ |check_log_space|Use: main|Type: integer|Default: 0| -+---------------+---------+-------------+----------+ ++--------------------------------------------------+ See check_spool_space below. -+--------------------+---------+-------------+-------------+ ++----------------------------------------------------------+ |check_rfc2047_length|Use: main|Type: boolean|Default: true| -+--------------------+---------+-------------+-------------+ ++----------------------------------------------------------+ RFC 2047 defines a way of encoding non-ASCII characters in headers using a system of "encoded words". The RFC specifies a maximum length for an encoded @@ -12719,15 +12895,15 @@ of the RFC, generates overlong encoded words. If check_rfc2047_length is set false, Exim recognizes encoded words of any length. -+------------------+---------+-------------+----------+ ++-----------------------------------------------------+ |check_spool_inodes|Use: main|Type: integer|Default: 0| -+------------------+---------+-------------+----------+ ++-----------------------------------------------------+ See check_spool_space below. -+-----------------+---------+-------------+----------+ ++----------------------------------------------------+ |check_spool_space|Use: main|Type: integer|Default: 0| -+-----------------+---------+-------------+----------+ ++----------------------------------------------------+ The four check_... options allow for checking of disk resources before a message is accepted. @@ -12764,17 +12940,17 @@ failure a message is written to stderr and Exim exits with a non-zero code, as it obviously cannot send an error message of any kind. -+-----------------+---------+------------+---------------+ ++--------------------------------------------------------+ |daemon_smtp_ports|Use: main|Type: string|Default: "smtp"| -+-----------------+---------+------------+---------------+ ++--------------------------------------------------------+ This option specifies one or more default SMTP ports on which the Exim daemon listens. See chapter 13 for details of how it is used. For backward compatibility, daemon_smtp_port (singular) is a synonym. -+----------------------+---------+-------------+----------+ ++---------------------------------------------------------+ |daemon_startup_retries|Use: main|Type: integer|Default: 9| -+----------------------+---------+-------------+----------+ ++---------------------------------------------------------+ This option, along with daemon_startup_sleep, controls the retrying done by the daemon at startup when it cannot immediately bind a listening socket (typically @@ -12782,15 +12958,15 @@ number of retries after the first failure, and daemon_startup_sleep defines the length of time to wait between retries. -+--------------------+---------+----------+------------+ ++------------------------------------------------------+ |daemon_startup_sleep|Use: main|Type: time|Default: 30s| -+--------------------+---------+----------+------------+ ++------------------------------------------------------+ See daemon_startup_retries. -+-------------+---------+---------------+------------+ ++----------------------------------------------------+ |delay_warning|Use: main|Type: time list|Default: 24h| -+-------------+---------+---------------+------------+ ++----------------------------------------------------+ When a message is delayed, Exim sends a warning message to the sender at intervals specified by this option. The data is a colon-separated list of times @@ -12818,9 +12994,9 @@ which depends on retry and queue-runner configuration. Typically retries will be configured more frequently than warning messages. -+-----------------------+---------+-------------+------------------+ ++------------------------------------------------------------------+ |delay_warning_condition|Use: main|Type: string*|Default: see below| -+-----------------------+---------+-------------+------------------+ ++------------------------------------------------------------------+ The string is expanded at the time a warning message might be sent. If all the deferred addresses have the same domain, it is set in $domain during the @@ -12840,28 +13016,28 @@ Precedence: header, or have "auto-generated" or "auto-replied" in an Auto-Submitted: header. -+----------------------+---------+-------------+--------------+ ++-------------------------------------------------------------+ |deliver_drop_privilege|Use: main|Type: boolean|Default: false| -+----------------------+---------+-------------+--------------+ ++-------------------------------------------------------------+ If this option is set true, Exim drops its root privilege at the start of a delivery process, and runs as the Exim user throughout. This severely restricts the kinds of local delivery that are possible, but is viable in certain types of configuration. There is a discussion about the use of root privilege in -chapter 54. +chapter 55. -+----------------------+---------+-----------------+--------------+ ++-----------------------------------------------------------------+ |deliver_queue_load_max|Use: main|Type: fixed-point|Default: unset| -+----------------------+---------+-----------------+--------------+ ++-----------------------------------------------------------------+ When this option is set, a queue run is abandoned if the system load average becomes greater than the value of the option. The option has no effect on ancient operating systems on which Exim cannot determine the load average. See also queue_only_load and smtp_load_reserve. -+--------------------+---------+-------------+-------------+ ++----------------------------------------------------------+ |delivery_date_remove|Use: main|Type: boolean|Default: true| -+--------------------+---------+-------------+-------------+ ++----------------------------------------------------------+ Exim's transports have an option for adding a Delivery-date: header to a message when it is delivered, in exactly the same way as Return-path: is @@ -12870,9 +13046,9 @@ removed at the time the message is received, to avoid any problems that might occur when a delivered message is subsequently sent on to some other recipient. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |disable_fsync|Use: main|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option is available only if Exim was built with the compile-time option ENABLE_DISABLE_FSYNC. When this is not set, a reference to disable_fsync in a @@ -12886,9 +13062,9 @@ crashes and power outages may cause data to be lost or scrambled. Here be Dragons. Beware. -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ |disable_ipv6|Use: main|Type: boolean|Default: false| -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ If this option is set true, even if the Exim binary has IPv6 support, no IPv6 activities take place. AAAA records are never looked up, and any IPv6 addresses @@ -12896,9 +13072,17 @@ ignored. If IP literals are enabled, the ipliteral router declines to handle IPv6 literal addresses. -+------------------------+---------+------------------+--------------+ ++-----------------------------------------------------------------------+ +|dkim_verify_signers|Use: main|Type: domain list*|Default: $dkim_signers| ++-----------------------------------------------------------------------+ + +This option gives a list of DKIM domains for which the DKIM ACL is run. It is +expanded after the message is received; by default it runs the ACL once for +each signature in the message. See chapter 57. + ++--------------------------------------------------------------------+ |dns_again_means_nonexist|Use: main|Type: domain list*|Default: unset| -+------------------------+---------+------------------+--------------+ ++--------------------------------------------------------------------+ DNS lookups give a "try again" response for the DNS errors "non-authoritative host not found" and "SERVERFAIL". This can cause Exim to keep trying to deliver @@ -12918,9 +13102,9 @@ SRV records give temporary errors. These more specific options are applied after this global option. -+-----------------------+---------+------------+------------------+ ++-----------------------------------------------------------------+ |dns_check_names_pattern|Use: main|Type: string|Default: see below| -+-----------------------+---------+------------+------------------+ ++-----------------------------------------------------------------+ When this option is set to a non-empty string, it causes Exim to check domain names for characters that are not allowed in host names before handing them to @@ -12939,24 +13123,24 @@ accessed in Exim by using a dnsdb lookup). If you set allow_utf8_domains, you must modify this pattern, or set the option to an empty string. -+--------------------+---------+-------------+----------+ ++-------------------------------------------------------+ |dns_csa_search_limit|Use: main|Type: integer|Default: 5| -+--------------------+---------+-------------+----------+ ++-------------------------------------------------------+ This option controls the depth of parental searching for CSA SRV records in the -DNS, as described in more detail in section 42.50. +DNS, as described in more detail in section 43.50. -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ |dns_csa_use_reverse|Use: main|Type: boolean|Default: true| -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ This option controls whether or not an IP address, given as a CSA domain, is reversed and looked up in the reverse DNS, as described in more detail in -section 42.50. +section 43.50. -+-------------+---------+-------------+-----------+ ++-------------------------------------------------+ |dns_dnssec_ok|Use: main|Type: integer|Default: -1| -+-------------+---------+-------------+-----------+ ++-------------------------------------------------+ If this option is set to a non-negative number then Exim will initialise the DNS resolver library to either use or not use DNSSEC, overriding the system @@ -12964,9 +13148,9 @@ If the resolver library does not support DNSSEC then this option has no effect. -+---------------+---------+------------------+--------------+ ++-----------------------------------------------------------+ |dns_ipv4_lookup|Use: main|Type: domain list*|Default: unset| -+---------------+---------+------------------+--------------+ ++-----------------------------------------------------------+ When Exim is compiled with IPv6 support and disable_ipv6 is not set, it looks for IPv6 address records (AAAA records) as well as IPv4 address records (A @@ -12977,9 +13161,9 @@ not work for the AAAA record type. In due course, when the world's name servers have all been upgraded, there should be no need for this option. -+-----------+---------+----------+-----------+ ++--------------------------------------------+ |dns_retrans|Use: main|Type: time|Default: 0s| -+-----------+---------+----------+-----------+ ++--------------------------------------------+ The options dns_retrans and dns_retry can be used to set the retransmission and retry parameters for DNS lookups. Values of zero (the defaults) leave the @@ -12990,15 +13174,42 @@ available in the external resolver interface structure, but nowhere does it seem to describe how they are used or what you might want to set in them. -+---------+---------+-------------+----------+ +See also the slow_lookup_log option. + ++--------------------------------------------+ |dns_retry|Use: main|Type: integer|Default: 0| -+---------+---------+-------------+----------+ ++--------------------------------------------+ See dns_retrans above. -+-------------+---------+-------------+-----------+ ++--------------------------------------------------------+ +|dns_trust_aa|Use: main|Type: domain list*|Default: unset| ++--------------------------------------------------------+ + +If this option is set then lookup results marked with the AA bit (Authoritative +Answer) are trusted the same way as if they were DNSSEC-verified. The authority +section's name of the answer must match with this expanded domain list. + +Use this option only if you talk directly to a resolver that is authoritive for +some zones and does not set the AD (Authentic Data) bit in the answer. Some DNS +servers may have an configuration option to mark the answers from their own +zones as verified (they set the AD bit). Others do not have this option. It is +considered as poor practice using a resolver that is an authoritive server for +some zones. + +Use this option only if you really have to (e.g. if you want to use DANE for +remote delivery to a server that is listed in the DNS zones that your resolver +is authoritive for). + +If the DNS answer packet has the AA bit set and contains resource record in the +answer section, the name of the first NS record appearing in the authority +section is compared against the list. If the answer packet is authoritive but +the answer section is empty, the name of the first SOA record in the +authoritive section is used instead. + ++-------------------------------------------------+ |dns_use_edns0|Use: main|Type: integer|Default: -1| -+-------------+---------+-------------+-----------+ ++-------------------------------------------------+ If this option is set to a non-negative number then Exim will initialise the DNS resolver library to either use or not use EDNS0 extensions, overriding the @@ -13006,17 +13217,27 @@ If the resolver library does not support EDNS0 then this option has no effect. -+-------+---------+-------------+--------------+ ++----------------------------------------------+ |drop_cr|Use: main|Type: boolean|Default: false| -+-------+---------+-------------+--------------+ ++----------------------------------------------+ This is an obsolete option that is now a no-op. It used to affect the way Exim handled CR and LF characters in incoming messages. What happens now is -described in section 46.2. +described in section 47.2. -+--------+---------+-------------+------------------+ ++-------------------------------------------------------------+ +|dsn_advertise_hosts|Use: main|Type: host list*|Default: unset| ++-------------------------------------------------------------+ + +DSN extensions (RFC3461) will be advertised in the EHLO message to, and +accepted from, these hosts. Hosts may use the NOTIFY and ENVID options on RCPT +TO commands, and RET and ORCPT options on MAIL FROM commands. A NOTIFY=SUCCESS +option requests success-DSN messages. A NOTIFY= option with no argument +requests that no delay or failure DSNs are sent. + ++---------------------------------------------------+ |dsn_from|Use: main|Type: string*|Default: see below| -+--------+---------+-------------+------------------+ ++---------------------------------------------------+ This option can be used to vary the contents of From: header lines in bounces and other automatically generated messages ("Delivery Status Notifications" - @@ -13027,9 +13248,9 @@ The value is expanded every time it is needed. If the expansion fails, a panic is logged, and the default value is used. -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ |envelope_to_remove|Use: main|Type: boolean|Default: true| -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ Exim's transports have an option for adding an Envelope-to: header to a message when it is delivered, in exactly the same way as Return-path: is handled. @@ -13039,9 +13260,9 @@ message is received, to avoid any problems that might occur when a delivered message is subsequently sent on to some other recipient. -+-----------+---------+------------------+--------------+ ++-------------------------------------------------------+ |errors_copy|Use: main|Type: string list*|Default: unset| -+-----------+---------+------------------+--------------+ ++-------------------------------------------------------+ Setting this option causes Exim to send bcc copies of bounce messages that it generates to other addresses. Note: This does not apply to bounce messages @@ -13065,9 +13286,9 @@ there was any wildcard matching in the pattern, the expansion variables $0, $1, etc. are set in the normal way. -+---------------+---------+------------+--------------+ ++-----------------------------------------------------+ |errors_reply_to|Use: main|Type: string|Default: unset| -+---------------+---------+------------+--------------+ ++-----------------------------------------------------+ By default, Exim's bounce and delivery warning messages contain the header line @@ -13088,19 +13309,19 @@ quota_warn_message option in an appendfile transport contain its own Reply-To: header line, the value of the errors_reply_to option is not used. -+----------+---------+------------+--------------------------------+ ++------------------------------------------------------------------+ |exim_group|Use: main|Type: string|Default: compile-time configured| -+----------+---------+------------+--------------------------------+ ++------------------------------------------------------------------+ This option changes the gid under which Exim runs when it gives up root privilege. The default value is compiled into the binary. The value of this option is used only when exim_user is also set. Unless it consists entirely of digits, the string is looked up using getgrnam(), and failure causes a -configuration error. See chapter 54 for a discussion of security issues. +configuration error. See chapter 55 for a discussion of security issues. -+---------+---------+------------+------------------+ ++---------------------------------------------------+ |exim_path|Use: main|Type: string|Default: see below| -+---------+---------+------------+------------------+ ++---------------------------------------------------+ This option specifies the path name of the Exim binary, which is used when Exim needs to re-exec itself. The default is set up to point to the file exim in the @@ -13111,9 +13332,9 @@ where the binary is. (They then use the -bP option to extract option settings such as the value of spool_directory.) -+---------+---------+------------+--------------------------------+ ++-----------------------------------------------------------------+ |exim_user|Use: main|Type: string|Default: compile-time configured| -+---------+---------+------------+--------------------------------+ ++-----------------------------------------------------------------+ This option changes the uid under which Exim runs when it gives up root privilege. The default value is compiled into the binary. Ownership of the run @@ -13122,20 +13343,20 @@ Unless it consists entirely of digits, the string is looked up using getpwnam() , and failure causes a configuration error. If exim_group is not also supplied, -the gid is taken from the result of getpwnam() if it is used. See chapter 54 +the gid is taken from the result of getpwnam() if it is used. See chapter 55 for a discussion of security issues. -+----------------------+---------+-----------------+--------------+ ++-----------------------------------------------------------------+ |extra_local_interfaces|Use: main|Type: string list|Default: unset| -+----------------------+---------+-----------------+--------------+ ++-----------------------------------------------------------------+ This option defines network interfaces that are to be considered local when routing, but which are not used for listening by the daemon. See section 13.8 for details. -+-------------------------------------+---------+-------------+-------------+ ++---------------------------------------------------------------------------+ |extract_addresses_remove_ arguments|Use: main|Type: boolean|Default: true| -+-------------------------------------+---------+-------------+-------------+ ++---------------------------------------------------------------------------+ According to some Sendmail documentation (Sun, IRIX, HP-UX), if any addresses are present on the command line when the -t option is used to build an envelope @@ -13147,9 +13368,9 @@ argument headers. If it is set false, Exim adds rather than removes argument addresses. -+----------------+---------+-------------+----------+ ++---------------------------------------------------+ |finduser_retries|Use: main|Type: integer|Default: 0| -+----------------+---------+-------------+----------+ ++---------------------------------------------------+ On systems running NIS or other schemes in which user and group information is distributed from a remote system, there can be times when getpwnam() and @@ -13162,9 +13383,9 @@ a traditional /etc/passwd file, because it will cause Exim needlessly to search the file multiple times for non-existent users, and also cause delay. -+-----------+---------+----------------------------------+--------------+ ++-----------------------------------------------------------------------+ |freeze_tell|Use: main|Type: string list, comma separated|Default: unset| -+-----------+---------+----------------------------------+--------------+ ++-----------------------------------------------------------------------+ On encountering certain errors, or when configured to do so in a system filter, ACL, or special router, Exim freezes a message. This means that no further @@ -13180,9 +13401,9 @@ configure freezing in a filter or ACL, you must arrange for any logging that you require. -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ |gecos_name|Use: main|Type: string*|Default: unset| -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ Some operating systems, notably HP-UX, use the "gecos" field in the system password file to hold other information in addition to users' real names. Exim @@ -13203,15 +13424,15 @@ gecos_pattern = ([^,]*) gecos_name = $1 -+-------------+---------+------------+--------------+ ++---------------------------------------------------+ |gecos_pattern|Use: main|Type: string|Default: unset| -+-------------+---------+------------+--------------+ ++---------------------------------------------------+ See gecos_name above. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |gnutls_compat_mode|Use: main|Type: boolean|Default: unset| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ This option controls whether GnuTLS is used in compatibility mode in an Exim server. This reduces security slightly, but improves interworking with older @@ -13224,9 +13445,9 @@ See http://www.gnutls.org/manual/gnutls.html#Smart-cards-and-HSMs for documentation. -+---------------+---------+------------+------------------+ ++---------------------------------------------------------+ |headers_charset|Use: main|Type: string|Default: see below| -+---------------+---------+------------+------------------+ ++---------------------------------------------------------+ This option sets a default character set for translating from encoded MIME "words" in header lines, when referenced by an $h_xxx expansion item. The @@ -13234,26 +13455,26 @@ is ISO-8859-1. For more details see the description of header insertions in section 11.5. -+--------------+---------+-------------+------------------+ ++---------------------------------------------------------+ |header_maxsize|Use: main|Type: integer|Default: see below| -+--------------+---------+-------------+------------------+ ++---------------------------------------------------------+ This option controls the overall maximum size of a message's header section. The default is the value of HEADER_MAXSIZE in Local/Makefile; the default for that is 1M. Messages with larger header sections are rejected. -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ |header_line_maxsize|Use: main|Type: integer|Default: 0| -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ This option limits the length of any individual header line in a message, after all the continuations have been joined together. Messages with individual header lines that are longer than the limit are rejected. The default value of zero means "no limit". -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ |helo_accept_junk_hosts|Use: main|Type: host list*|Default: unset| -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ Exim checks the syntax of HELO and EHLO commands for incoming SMTP mail, and gives an error response for invalid data. Unfortunately, there are some SMTP @@ -13262,9 +13483,9 @@ want to do semantic checking. See also helo_allow_chars for a way of extending the permitted character set. -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ |helo_allow_chars|Use: main|Type: string|Default: unset| -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ This option can be set to a string of rogue characters that are permitted in all EHLO and HELO names in addition to the standard letters, digits, hyphens, @@ -13274,18 +13495,18 @@ Note that the value is one string, not a list. -+-------------------+---------+------------------+----------------+ ++-----------------------------------------------------------------+ |helo_lookup_domains|Use: main|Type: domain list*|Default: "@:@[]"| -+-------------------+---------+------------------+----------------+ ++-----------------------------------------------------------------+ If the domain given by a client in a HELO or EHLO command matches this list, a reverse lookup is done in order to establish the host's true name. The default forces a lookup if the client host gives the server's name or any of its IP addresses (in brackets), something that broken clients have been seen to do. -+---------------------+---------+----------------+--------------+ ++---------------------------------------------------------------+ |helo_try_verify_hosts|Use: main|Type: host list*|Default: unset| -+---------------------+---------+----------------+--------------+ ++---------------------------------------------------------------+ By default, Exim just checks the syntax of HELO and EHLO commands (see helo_accept_junk_hosts and helo_allow_chars). However, some sites like to do @@ -13306,16 +13527,18 @@ * matches the host name that Exim obtains by doing a reverse lookup of the calling host address, or - * when looked up using gethostbyname() (or getipnodebyname() when available) - yields the calling host address. + * when looked up in DNS yields the calling host address. However, the EHLO or HELO command is not rejected if any of the checks fail. Processing continues, but the result of the check is remembered, and can be detected later in an ACL by the "verify = helo" condition. -+-----------------+---------+----------------+--------------+ +If DNS was used for successful verification, the variable $helo_verify_dnssec +records the DNSSEC status of the lookups. + ++-----------------------------------------------------------+ |helo_verify_hosts|Use: main|Type: host list*|Default: unset| -+-----------------+---------+----------------+--------------+ ++-----------------------------------------------------------+ Like helo_try_verify_hosts, this option is obsolete, and retained only for backwards compatibility. For hosts that match this option, Exim checks the host @@ -13324,9 +13547,9 @@ entries are written to the main and reject logs. If a MAIL command is received before EHLO or HELO, it is rejected with a 503 error. -+------------+---------+------------------+--------------+ ++--------------------------------------------------------+ |hold_domains|Use: main|Type: domain list*|Default: unset| -+------------+---------+------------------+--------------+ ++--------------------------------------------------------+ This option allows mail for particular domains to be held on the queue manually. The option is overridden if a message delivery is forced with the -M, @@ -13346,9 +13569,9 @@ any retry rule. If you want to hold messages for longer than the normal retry times, insert a dummy retry rule with a long retry time. -+-----------+---------+----------------+--------------+ ++-----------------------------------------------------+ |host_lookup|Use: main|Type: host list*|Default: unset| -+-----------+---------+----------------+--------------+ ++-----------------------------------------------------+ Exim does not look up the name of a calling host from its IP address unless it is required to compare against some host list, or the host matches @@ -13370,9 +13593,9 @@ dns_again_means_nonexist, helo_lookup_domains, and "verify = reverse_host_lookup" in ACLs. -+-----------------+---------+-----------------+-----------------------+ ++---------------------------------------------------------------------+ |host_lookup_order|Use: main|Type: string list|Default: "bydns:byaddr"| -+-----------------+---------+-----------------+-----------------------+ ++---------------------------------------------------------------------+ This option specifies the order of different lookup methods when Exim is trying to find a host name from an IP address. The default is to do a DNS lookup @@ -13385,9 +13608,9 @@ Different operating systems give different results in this case. That is why the default tries a DNS lookup first. -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ |host_reject_connection|Use: main|Type: host list*|Default: unset| -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ If this option is set, incoming SMTP calls from the hosts listed are rejected as soon as the connection is made. This option is obsolete, and retained only @@ -13397,12 +13620,12 @@ The ability to give an immediate rejection (either by this option or using an ACL) is provided for use in unusual cases. Many hosts will just try again, sometimes without much delay. Normally, it is better to use an ACL to reject -incoming messages at a later stage, such as after RCPT commands. See chapter 42 +incoming messages at a later stage, such as after RCPT commands. See chapter 43 . -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ |hosts_connection_nolog|Use: main|Type: host list*|Default: unset| -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ This option defines a list of hosts for which connection logging does not happen, even though the smtp_connection log selector is set. For example, you @@ -13416,9 +13639,9 @@ If the smtp_connection log selector is not set, this option has no effect. -+--------------------+---------+------------------+--------------+ ++----------------------------------------------------------------+ |hosts_treat_as_local|Use: main|Type: domain list*|Default: unset| -+--------------------+---------+------------------+--------------+ ++----------------------------------------------------------------+ If this option is set, any host names that match the domain list are treated as if they were the local host when Exim is scanning host lists obtained from MX @@ -13433,17 +13656,17 @@ extra_local_interfaces, and chapter 13, which contains a discussion about local network interfaces and recognizing the local host. -+-------------+---------+-----------------+--------------+ ++--------------------------------------------------------+ |ibase_servers|Use: main|Type: string list|Default: unset| -+-------------+---------+-----------------+--------------+ ++--------------------------------------------------------+ This option provides a list of InterBase servers and associated connection -data, to be used in conjunction with ibase lookups (see section 9.21). The +data, to be used in conjunction with ibase lookups (see section 9.22). The option is available only if Exim has been built with InterBase support. -+--------------------------+---------+----------+------------+ ++------------------------------------------------------------+ |ignore_bounce_errors_after|Use: main|Type: time|Default: 10w| -+--------------------------+---------+----------+------------+ ++------------------------------------------------------------+ This option affects the processing of bounce messages that cannot be delivered, that is, those that suffer a permanent delivery failure. (Bounce messages that @@ -13466,9 +13689,9 @@ dealing with other kinds of frozen message, see auto_thaw and timeout_frozen_after. -+---------------------+---------+----------------+--------------+ ++---------------------------------------------------------------+ |ignore_fromline_hosts|Use: main|Type: host list*|Default: unset| -+---------------------+---------+----------------+--------------+ ++---------------------------------------------------------------+ Some broken SMTP clients insist on sending a UUCP-like "From " line before the headers of a message. By default this is treated as the start of the message's @@ -13478,84 +13701,84 @@ than a remote host, and is using -bs to inject the messages, ignore_fromline_local must be set to achieve this effect. -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ |ignore_fromline_local|Use: main|Type: boolean|Default: false| -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ See ignore_fromline_hosts above. -+--------------+---------+----------+-----------+ ++-----------------------------------------------+ |keep_malformed|Use: main|Type: time|Default: 4d| -+--------------+---------+----------+-----------+ ++-----------------------------------------------+ This option specifies the length of time to keep messages whose spool files have been corrupted in some way. This should, of course, never happen. At the next attempt to deliver such a message, it gets removed. The incident is logged. -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ |ldap_ca_cert_dir|Use: main|Type: string|Default: unset| -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ This option indicates which directory contains CA certificates for verifying a TLS certificate presented by an LDAP server. While Exim does not provide a default value, your SSL library may. Analogous to tls_verify_certificates but as a client-side option for LDAP and constrained to be a directory. -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ |ldap_ca_cert_file|Use: main|Type: string|Default: unset| -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ This option indicates which file contains CA certificates for verifying a TLS certificate presented by an LDAP server. While Exim does not provide a default value, your SSL library may. Analogous to tls_verify_certificates but as a client-side option for LDAP and constrained to be a file. -+--------------+---------+------------+--------------+ ++----------------------------------------------------+ |ldap_cert_file|Use: main|Type: string|Default: unset| -+--------------+---------+------------+--------------+ ++----------------------------------------------------+ This option indicates which file contains an TLS client certificate which Exim should present to the LDAP server during TLS negotiation. Should be used together with ldap_cert_key. -+-------------+---------+------------+--------------+ ++---------------------------------------------------+ |ldap_cert_key|Use: main|Type: string|Default: unset| -+-------------+---------+------------+--------------+ ++---------------------------------------------------+ This option indicates which file contains the secret/private key to use to prove identity to the LDAP server during TLS negotiation. Should be used together with ldap_cert_file, which contains the identity to be proven. -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ |ldap_cipher_suite|Use: main|Type: string|Default: unset| -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ This controls the TLS cipher-suite negotiation during TLS negotiation with the -LDAP server. See 41.4 for more details of the format of cipher-suite options +LDAP server. See 42.4 for more details of the format of cipher-suite options with OpenSSL (as used by LDAP client libraries). -+--------------------+---------+-----------------+--------------+ ++---------------------------------------------------------------+ |ldap_default_servers|Use: main|Type: string list|Default: unset| -+--------------------+---------+-----------------+--------------+ ++---------------------------------------------------------------+ This option provides a list of LDAP servers which are tried in turn when an -LDAP query does not contain a server. See section 9.14 for details of LDAP +LDAP query does not contain a server. See section 9.15 for details of LDAP queries. This option is available only when Exim has been built with LDAP support. -+-----------------+---------+------------+---------------+ ++--------------------------------------------------------+ |ldap_require_cert|Use: main|Type: string|Default: unset.| -+-----------------+---------+------------+---------------+ ++--------------------------------------------------------+ This should be one of the values "hard", "demand", "allow", "try" or "never". A value other than one of these is interpreted as "never". See the entry "TLS_REQCERT" in your system man page for ldap.conf(5). Although Exim does not set a default, the LDAP library probably defaults to hard/demand. -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ |ldap_start_tls|Use: main|Type: boolean|Default: false| -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ If set, Exim will attempt to negotiate TLS with the LDAP server when connecting on a regular LDAP port. This is the LDAP equivalent of SMTP's "STARTTLS". This @@ -13563,9 +13786,9 @@ the event of failure to negotiate TLS, the action taken is controlled by ldap_require_cert. -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ |ldap_version|Use: main|Type: integer|Default: unset| -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ This option can be used to force Exim to set a specific protocol version for LDAP. If it option is unset, it is shown by the -bP command line option as -1. @@ -13573,9 +13796,9 @@ headers; otherwise it is 2. This option is available only when Exim has been built with LDAP support. -+----------------+---------+-------------+-------------+ ++------------------------------------------------------+ |local_from_check|Use: main|Type: boolean|Default: true| -+----------------+---------+-------------+-------------+ ++------------------------------------------------------+ When a message is submitted locally (that is, not over a TCP/IP connection) by an untrusted user, Exim removes any existing Sender: header line, and checks @@ -13601,12 +13824,12 @@ untrusted_set_sender permits the user to supply an envelope sender. For messages received over TCP/IP, an ACL can specify "submission mode" to -request similar header line checking. See section 46.16, which has more details +request similar header line checking. See section 47.16, which has more details about Sender: processing. -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ |local_from_prefix|Use: main|Type: string|Default: unset| -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ When Exim checks the From: header line of locally submitted messages for matching the login id (see local_from_check above), it can be configured to @@ -13625,15 +13848,15 @@ the actual sender address that is constructed from the login name and qualify domain. -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ |local_from_suffix|Use: main|Type: string|Default: unset| -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ See local_from_prefix above. -+----------------+---------+-----------------+------------------+ ++---------------------------------------------------------------+ |local_interfaces|Use: main|Type: string list|Default: see below| -+----------------+---------+-----------------+------------------+ ++---------------------------------------------------------------+ This option controls which network interfaces are used by the daemon for listening; they are also used to identify the local host when routing. Chapter @@ -13647,30 +13870,30 @@ local_interfaces = <; ::0 ; 0.0.0.0 -+------------------+---------+----------+-----------+ ++---------------------------------------------------+ |local_scan_timeout|Use: main|Type: time|Default: 5m| -+------------------+---------+----------+-----------+ ++---------------------------------------------------+ -This timeout applies to the local_scan() function (see chapter 44). Zero means +This timeout applies to the local_scan() function (see chapter 45). Zero means "no timeout". If the timeout is exceeded, the incoming message is rejected with a temporary error if it is an SMTP message. For a non-SMTP message, the message is dropped and Exim ends with a non-zero code. The incident is logged on the main and reject logs. -+-------------------+---------+-------------+--------------+ ++----------------------------------------------------------+ |local_sender_retain|Use: main|Type: boolean|Default: false| -+-------------------+---------+-------------+--------------+ ++----------------------------------------------------------+ When a message is submitted locally (that is, not over a TCP/IP connection) by an untrusted user, Exim removes any existing Sender: header line. If you do not want this to happen, you must set local_sender_retain, and you must also set local_from_check to be false (Exim will complain if you do not). See also the -ACL modifier "control = suppress_local_fixups". Section 46.16 has more details +ACL modifier "control = suppress_local_fixups". Section 47.16 has more details about Sender: processing. -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ |localhost_number|Use: main|Type: string*|Default: unset| -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ Exim's message ids are normally unique only within the local host. If uniqueness among a set of hosts is required, each host must set a different @@ -13683,26 +13906,27 @@ the message id, instead of just being a fractional part of the time, are computed from the time and the local host number as described in section 3.4. -+-------------+---------+------------------+----------------------------+ ++-----------------------------------------------------------------------+ |log_file_path|Use: main|Type: string list*|Default: set at compile time| -+-------------+---------+------------------+----------------------------+ ++-----------------------------------------------------------------------+ This option sets the path which is used to determine the names of Exim's log files, or indicates that logging is to be to syslog, or both. It is expanded when Exim is entered, so it can, for example, contain a reference to the host -name. If no specific path is set for the log files at compile or run time, they -are written in a sub-directory called log in Exim's spool directory. Chapter 51 -contains further details about Exim's logging, and section 51.1 describes how -the contents of log_file_path are used. If this string is fixed at your +name. If no specific path is set for the log files at compile or run time, or +if the option is unset at run time (i.e. "log_file_path = ") they are written +in a sub-directory called log in Exim's spool directory. Chapter 52 contains +further details about Exim's logging, and section 52.1 describes how the +contents of log_file_path are used. If this string is fixed at your installation (contains no expansion variables) it is recommended that you do not set this option in the configuration file, but instead supply the path using LOG_FILE_PATH in Local/Makefile so that it is available to Exim for logging errors detected early on - in particular, failure to read the configuration file. -+------------+---------+------------+--------------+ ++--------------------------------------------------+ |log_selector|Use: main|Type: string|Default: unset| -+------------+---------+------------+--------------+ ++--------------------------------------------------+ This option can be used to reduce or increase the number of things that Exim writes to its log files. Its argument is made up of names preceded by plus or @@ -13711,11 +13935,11 @@ log_selector = +arguments -retry_defer A list of possible names and what they control is given in the chapter on -logging, in section 51.15. +logging, in section 52.15. -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ |log_timezone|Use: main|Type: boolean|Default: false| -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ By default, the timestamps on log lines are in local time without the timezone. This means that if your timezone changes twice a year, the timestamps in log @@ -13727,9 +13951,9 @@ $tod_log variable contains the log timestamp without the zone, but there is another variable called $tod_zone that contains just the timezone offset. -+---------------+---------+-------------+-----------+ ++---------------------------------------------------+ |lookup_open_max|Use: main|Type: integer|Default: 25| -+---------------+---------+-------------+-----------+ ++---------------------------------------------------+ This option limits the number of simultaneously open files for single-key lookups that use regular files (that is, lsearch, dbm, and cdb). Exim normally @@ -13740,33 +13964,33 @@ of lookup_open_max. If you are getting "too many open files" errors with NDBM, you need to reduce the value of lookup_open_max. -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ |max_username_length|Use: main|Type: integer|Default: 0| -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ Some operating systems are broken in that they truncate long arguments to getpwnam() to eight characters, instead of returning "no such user". If this option is set greater than zero, any attempt to call getpwnam() with an argument that is longer behaves as if getpwnam() failed. -+---------------------+---------+----------+--------------+ ++---------------------------------------------------------+ |message_body_newlines|Use: main|Type: bool|Default: false| -+---------------------+---------+----------+--------------+ ++---------------------------------------------------------+ By default, newlines in the message body are replaced by spaces when setting the $message_body and $message_body_end expansion variables. If this option is set true, this no longer happens. -+--------------------+---------+-------------+------------+ ++---------------------------------------------------------+ |message_body_visible|Use: main|Type: integer|Default: 500| -+--------------------+---------+-------------+------------+ ++---------------------------------------------------------+ This option specifies how much of a message's body is to be included in the $message_body and $message_body_end expansion variables. -+------------------------+---------+-------------+--------------+ ++---------------------------------------------------------------+ |message_id_header_domain|Use: main|Type: string*|Default: unset| -+------------------------+---------+-------------+--------------+ ++---------------------------------------------------------------+ If this option is set, the string is expanded and used as the right hand side (domain) of the Message-ID: header that Exim creates if a locally-originated @@ -13776,9 +14000,9 @@ the expansion is forced to fail, or if the result is an empty string, the option is ignored. -+----------------------+---------+-------------+--------------+ ++-------------------------------------------------------------+ |message_id_header_text|Use: main|Type: string*|Default: unset| -+----------------------+---------+-------------+--------------+ ++-------------------------------------------------------------+ If this variable is set, the string is expanded and used to augment the text of the Message-id: header that Exim creates if a locally-originated incoming @@ -13792,9 +14016,9 @@ means that variables such as $tod_log can be used, because the spaces and colons will become hyphens. -+------------+---------+-------------+-------------+ ++--------------------------------------------------+ |message_logs|Use: main|Type: boolean|Default: true| -+------------+---------+-------------+-------------+ ++--------------------------------------------------+ If this option is turned off, per-message log files are not created in the msglog spool sub-directory. This reduces the amount of disk I/O required by @@ -13803,9 +14027,9 @@ per-message log) to three. The other major I/O activity is Exim's main log, which is not affected by this option. -+------------------+---------+-------------+------------+ ++-------------------------------------------------------+ |message_size_limit|Use: main|Type: string*|Default: 50M| -+------------------+---------+-------------+------------+ ++-------------------------------------------------------+ This option limits the maximum size of message that Exim will process. The value is expanded for each incoming connection so, for example, it can be made @@ -13838,9 +14062,9 @@ SIZE extension in an EHLO response, but without a limit, so as to permit SMTP clients to still indicate the message size along with the MAIL verb. -+--------------------+---------+-------------+--------------+ ++-----------------------------------------------------------+ |move_frozen_messages|Use: main|Type: boolean|Default: false| -+--------------------+---------+-------------+--------------+ ++-----------------------------------------------------------+ This option, which is available only if Exim has been built with the setting @@ -13852,25 +14076,25 @@ for handling such moved messages, and they do not show up in lists generated by -bp or by the Exim monitor. -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ |mua_wrapper|Use: main|Type: boolean|Default: false| -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ Setting this option true causes Exim to run in a very restrictive mode in which -it passes messages synchronously to a smart host. Chapter 50 contains a full +it passes messages synchronously to a smart host. Chapter 51 contains a full description of this facility. -+-------------+---------+-----------------+--------------+ ++--------------------------------------------------------+ |mysql_servers|Use: main|Type: string list|Default: unset| -+-------------+---------+-----------------+--------------+ ++--------------------------------------------------------+ This option provides a list of MySQL servers and associated connection data, to -be used in conjunction with mysql lookups (see section 9.21). The option is +be used in conjunction with mysql lookups (see section 9.22). The option is available only if Exim has been built with MySQL support. -+-----------+---------+------------------+--------------+ ++-------------------------------------------------------+ |never_users|Use: main|Type: string list*|Default: unset| -+-----------+---------+------------------+--------------+ ++-------------------------------------------------------+ This option is expanded just once, at the start of Exim's processing. Local message deliveries are normally run in processes that are setuid to the @@ -13894,9 +14118,9 @@ harm. This option overrides the pipe_as_creator option of the pipe transport driver. -+---------------+---------+-----------------+------------------+ ++--------------------------------------------------------------+ |openssl_options|Use: main|Type: string list|Default: +no_sslv2| -+---------------+---------+-----------------+------------------+ ++--------------------------------------------------------------+ This option allows an administrator to adjust the SSL options applied by OpenSSL to connections. It is given as a space-separated list of items, each @@ -13916,17 +14140,22 @@ lightly. An unrecognised item will be detected at startup, by invoking Exim with the -bV flag. +The option affects Exim operating both as a server and as a client. + Historical note: prior to release 4.80, Exim defaulted this value to "+dont_insert_empty_fragments", which may still be needed for compatibility with some clients, but which lowers security by increasing exposure to some now infamous attacks. -An example: +Examples: # Make both old MS and old Eudora happy: openssl_options = -all +microsoft_big_sslv3_buffer \ +dont_insert_empty_fragments +# Disable older protocol versions: +openssl_options = +no_sslv2 +no_sslv3 + Possible options may include: * "all" @@ -13990,17 +14219,17 @@ release is new enough to contain this work-around. This may be a situation where you have to upgrade OpenSSL to get buggy clients working. -+--------------+---------+-----------------+--------------+ ++---------------------------------------------------------+ |oracle_servers|Use: main|Type: string list|Default: unset| -+--------------+---------+-----------------+--------------+ ++---------------------------------------------------------+ This option provides a list of Oracle servers and associated connection data, -to be used in conjunction with oracle lookups (see section 9.21). The option is +to be used in conjunction with oracle lookups (see section 9.22). The option is available only if Exim has been built with Oracle support. -+--------------------+---------+------------------+--------------+ ++----------------------------------------------------------------+ |percent_hack_domains|Use: main|Type: domain list*|Default: unset| -+--------------------+---------+------------------+--------------+ ++----------------------------------------------------------------+ The "percent hack" is the convention whereby a local part containing a percent sign is re-interpreted as a new email address, with the percent replaced by @. @@ -14017,31 +14246,31 @@ to reject recipient addresses with percent characters in their local parts. Exim's default configuration does this. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |perl_at_start|Use: main|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option is available only when Exim is built with an embedded Perl interpreter. See chapter 12 for details of its use. -+------------+---------+------------+--------------+ ++--------------------------------------------------+ |perl_startup|Use: main|Type: string|Default: unset| -+------------+---------+------------+--------------+ ++--------------------------------------------------+ This option is available only when Exim is built with an embedded Perl interpreter. See chapter 12 for details of its use. -+-------------+---------+-----------------+--------------+ ++--------------------------------------------------------+ |pgsql_servers|Use: main|Type: string list|Default: unset| -+-------------+---------+-----------------+--------------+ ++--------------------------------------------------------+ This option provides a list of PostgreSQL servers and associated connection -data, to be used in conjunction with pgsql lookups (see section 9.21). The +data, to be used in conjunction with pgsql lookups (see section 9.22). The option is available only if Exim has been built with PostgreSQL support. -+-------------+---------+-------------+----------------------------+ ++------------------------------------------------------------------+ |pid_file_path|Use: main|Type: string*|Default: set at compile time| -+-------------+---------+-------------+----------------------------+ ++------------------------------------------------------------------+ This option sets the name of the file to which the Exim daemon writes its process id. The string is expanded, so it can contain, for example, references @@ -14054,31 +14283,31 @@ command line option. A pid file is not written if a "non-standard" daemon is run by means of the -oX option, unless a path is explicitly supplied by -oP. -+--------------------------+---------+----------------+----------+ ++----------------------------------------------------------------+ |pipelining_advertise_hosts|Use: main|Type: host list*|Default: *| -+--------------------------+---------+----------------+----------+ ++----------------------------------------------------------------+ This option can be used to suppress the advertisement of the SMTP PIPELINING extension to specific hosts. See also the no_pipelining control in section -42.22. When PIPELINING is not advertised and smtp_enforce_sync is true, an Exim +43.22. When PIPELINING is not advertised and smtp_enforce_sync is true, an Exim server enforces strict synchronization for each SMTP command and response. When PIPELINING is advertised, Exim assumes that clients will use it; "out of order" commands that are "expected" do not count as protocol errors (see smtp_max_synprot_errors). -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ |prdr_enable|Use: main|Type: boolean|Default: false| -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ This option can be used to enable the Per-Recipient Data Response extension to SMTP, defined by Eric Hall. If the option is set, PRDR is advertised by Exim when operating as a server. If the client requests PRDR, and more than one recipient, for a message an additional ACL is called for each recipient after -the message content is recieved. See section 42.9. +the message content is recieved. See section 43.9. -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ |preserve_message_logs|Use: main|Type: boolean|Default: false| -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ If this option is set, message log files are not deleted when messages are completed. Instead, they are moved to a sub-directory of the spool directory @@ -14086,9 +14315,9 @@ purposes. This is a dangerous option to set on systems with any appreciable volume of mail. Use with care! -+----------------+---------+------------+------------------+ ++----------------------------------------------------------+ |primary_hostname|Use: main|Type: string|Default: see below| -+----------------+---------+------------+------------------+ ++----------------------------------------------------------+ This specifies the name of the current host. It is used in the default EHLO or HELO command for outgoing SMTP messages (changeable via the helo_data option in @@ -14103,9 +14332,9 @@ $primary_hostname contains the host name, whether set explicitly by this option, or defaulted. -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ |print_topbitchars|Use: main|Type: boolean|Default: false| -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ By default, Exim considers only those characters whose codes lie in the range 32-126 to be printing characters. In a number of circumstances (for example, @@ -14116,13 +14345,13 @@ This option also affects the header syntax checks performed by the autoreply transport, and whether Exim uses RFC 2047 encoding of the user's full name when -constructing From: and Sender: addresses (as described in section 46.18). +constructing From: and Sender: addresses (as described in section 47.18). Setting this option can cause Exim to generate eight bit message headers that do not conform to the standards. -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ |process_log_path|Use: main|Type: string|Default: unset| -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ This option sets the name of the file to which an Exim process writes its "process log" when sent a USR1 signal. This is used by the exiwhat utility @@ -14131,16 +14360,16 @@ useful in environments where two different Exims are running, using different spool directories. -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ |prod_requires_admin|Use: main|Type: boolean|Default: true| -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ The -M, -R, and -q command-line options require the caller to be an admin user unless prod_requires_admin is set false. See also queue_list_requires_admin. -+--------------+---------+------------+------------------+ ++--------------------------------------------------------+ |qualify_domain|Use: main|Type: string|Default: see below| -+--------------+---------+------------+------------------+ ++--------------------------------------------------------+ This option specifies the domain name that is added to any envelope sender addresses that do not have a domain qualification. It also applies to recipient @@ -14156,33 +14385,33 @@ Internally, Exim always works with fully qualified envelope addresses. If qualify_domain is not set, it defaults to the primary_hostname value. -+-----------------+---------+------------+------------------+ ++-----------------------------------------------------------+ |qualify_recipient|Use: main|Type: string|Default: see below| -+-----------------+---------+------------+------------------+ ++-----------------------------------------------------------+ This option allows you to specify a different domain for qualifying recipient addresses to the one that is used for senders. See qualify_domain above. -+-------------+---------+------------------+--------------+ ++---------------------------------------------------------+ |queue_domains|Use: main|Type: domain list*|Default: unset| -+-------------+---------+------------------+--------------+ ++---------------------------------------------------------+ This option lists domains for which immediate delivery is not required. A delivery process is started whenever a message is received, but only those domains that do not match are processed. All other deliveries wait until the next queue run. See also hold_domains and queue_smtp_domains. -+-------------------------+---------+-------------+-------------+ ++---------------------------------------------------------------+ |queue_list_requires_admin|Use: main|Type: boolean|Default: true| -+-------------------------+---------+-------------+-------------+ ++---------------------------------------------------------------+ The -bp command-line option, which lists the messages that are on the queue, requires the caller to be an admin user unless queue_list_requires_admin is set false. See also prod_requires_admin. -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ |queue_only|Use: main|Type: boolean|Default: false| -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ If queue_only is set, a delivery process is not automatically started whenever a message is received. Instead, the message waits on the queue for the next @@ -14193,9 +14422,9 @@ command line options override queue_only unless queue_only_override is set false. See also queue_only_file, queue_only_load, and smtp_accept_queue. -+---------------+---------+------------+--------------+ ++-----------------------------------------------------+ |queue_only_file|Use: main|Type: string|Default: unset| -+---------------+---------+------------+--------------+ ++-----------------------------------------------------+ This option can be set to a colon-separated list of absolute path names, each one optionally preceded by "smtp". When Exim is receiving a message, it tests @@ -14209,9 +14438,9 @@ causes Exim to behave as if queue_smtp_domains were set to "*" whenever /some/ file exists. -+---------------+---------+-----------------+--------------+ ++----------------------------------------------------------+ |queue_only_load|Use: main|Type: fixed-point|Default: unset| -+---------------+---------+-----------------+--------------+ ++----------------------------------------------------------+ If the system load average is higher than this value, incoming messages from all sources are queued, and no automatic deliveries are started. If this @@ -14224,9 +14453,9 @@ determine the load average. See also deliver_queue_load_max and smtp_load_reserve. -+---------------------+---------+-------------+-------------+ ++-----------------------------------------------------------+ |queue_only_load_latch|Use: main|Type: boolean|Default: true| -+---------------------+---------+-------------+-------------+ ++-----------------------------------------------------------+ When this option is true (the default), once one message has been queued because the load average is higher than the value set by queue_only_load, all @@ -14239,18 +14468,18 @@ should be set false. This causes the value of the load average to be re-evaluated for each message. -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ |queue_only_override|Use: main|Type: boolean|Default: true| -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ When this option is true, the -odx command line options override the setting of queue_only or queue_only_file in the configuration file. If queue_only_override is set false, the -odx options cannot be used to override; they are accepted, but ignored. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |queue_run_in_order|Use: main|Type: boolean|Default: false| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set, queue runs happen in order of message arrival instead of in an arbitrary order. For this to happen, a complete list of the entire queue @@ -14264,9 +14493,9 @@ queue is large, because of the extra work in setting up the single, large list. In most situations, queue_run_in_order should not be set. -+-------------+---------+-------------+----------+ ++------------------------------------------------+ |queue_run_max|Use: main|Type: integer|Default: 5| -+-------------+---------+-------------+----------+ ++------------------------------------------------+ This controls the maximum number of queue runner processes that an Exim daemon can run simultaneously. This does not mean that it starts them all at once, but @@ -14281,9 +14510,9 @@ run. If you do not want queue runs to occur, omit the -qxx setting on the daemon's command line. -+------------------+---------+------------------+--------------+ ++--------------------------------------------------------------+ |queue_smtp_domains|Use: main|Type: domain list*|Default: unset| -+------------------+---------+------------------+--------------+ ++--------------------------------------------------------------+ When this option is set, a delivery process is started whenever a message is received, routing is performed, and local deliveries take place. However, if @@ -14296,9 +14525,9 @@ queued in this way, and is equivalent to setting queue_smtp_domains to "*". See also hold_domains and queue_domains. -+---------------+---------+----------+-----------+ ++------------------------------------------------+ |receive_timeout|Use: main|Type: time|Default: 0s| -+---------------+---------+----------+-----------+ ++------------------------------------------------+ This option sets the timeout for accepting a non-SMTP message, that is, the maximum time that Exim waits when reading a message on the standard input. If @@ -14306,9 +14535,9 @@ command line option. The timeout for incoming SMTP messages is controlled by smtp_receive_timeout. -+--------------------+---------+-------------+------------------+ ++---------------------------------------------------------------+ |received_header_text|Use: main|Type: string*|Default: see below| -+--------------------+---------+-------------+------------------+ ++---------------------------------------------------------------+ This string defines the contents of the Received: message header that is added to each message, except for the timestamp, which is automatically added on at @@ -14350,18 +14579,18 @@ checks have taken place, the timestamp is updated to the time at which the message was accepted. -+--------------------+---------+-------------+-----------+ ++--------------------------------------------------------+ |received_headers_max|Use: main|Type: integer|Default: 30| -+--------------------+---------+-------------+-----------+ ++--------------------------------------------------------+ When a message is to be delivered, the number of Received: headers is counted, and if it is greater than this parameter, a mail loop is assumed to have occurred, the delivery is abandoned, and an error message is generated. This applies to both local and remote deliveries. -+---------------------------+---------+----------------+--------------+ ++---------------------------------------------------------------------+ |recipient_unqualified_hosts|Use: main|Type: host list*|Default: unset| -+---------------------------+---------+----------------+--------------+ ++---------------------------------------------------------------------+ This option lists those hosts from which Exim is prepared to accept unqualified recipient addresses in message envelopes. The addresses are made fully @@ -14371,9 +14600,9 @@ host that matches recipient_unqualified_hosts, or if the message was submitted locally (not using TCP/IP), and the -bnq option was not set. -+--------------+---------+-------------+----------+ ++-------------------------------------------------+ |recipients_max|Use: main|Type: integer|Default: 0| -+--------------+---------+-------------+----------+ ++-------------------------------------------------+ If this option is set greater than zero, it specifies the maximum number of original recipients for any message. Additional recipients that are generated @@ -14385,9 +14614,9 @@ Note: The RFCs specify that an SMTP server should accept at least 100 RCPT commands in a single message. -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ |recipients_max_reject|Use: main|Type: boolean|Default: false| -+---------------------+---------+-------------+--------------+ ++------------------------------------------------------------+ If this option is set true, Exim rejects SMTP messages containing too many recipients by giving 552 errors to the surplus RCPT commands, and a 554 error @@ -14396,9 +14625,9 @@ of recipients. The remote server should then re-send the message for the remaining recipients at a later time. -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ |remote_max_parallel|Use: main|Type: integer|Default: 2| -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ This option controls parallel delivery of one message to a number of remote hosts. If the value is less than 2, parallel delivery is disabled, and Exim @@ -14433,9 +14662,9 @@ before queueing, so that several messages for the same host will eventually get delivered down the same connection. -+-------------------+---------+------------------+--------------+ ++---------------------------------------------------------------+ |remote_sort_domains|Use: main|Type: domain list*|Default: unset| -+-------------------+---------+------------------+--------------+ ++---------------------------------------------------------------+ When there are a number of remote deliveries for a message, they are sorted by domain into the order given by this list. For example, @@ -14445,27 +14674,27 @@ would attempt to deliver to all addresses in the cam.ac.uk domain first, then to those in the uk domain, then to any others. -+-----------------+---------+----------+-----------+ ++--------------------------------------------------+ |retry_data_expire|Use: main|Type: time|Default: 7d| -+-----------------+---------+----------+-----------+ ++--------------------------------------------------+ This option sets a "use before" time on retry information in Exim's hints database. Any older retry data is ignored. This means that, for example, once a host has not been tried for 7 days, Exim behaves as if it has no knowledge of past failures. -+------------------+---------+----------+------------+ ++----------------------------------------------------+ |retry_interval_max|Use: main|Type: time|Default: 24h| -+------------------+---------+----------+------------+ ++----------------------------------------------------+ Chapter 32 describes Exim's mechanisms for controlling the intervals between delivery attempts for messages that cannot be delivered straight away. This option sets an overall limit to the length of time between retries. It cannot be set greater than 24 hours; any attempt to do so forces the default value. -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ |return_path_remove|Use: main|Type: boolean|Default: true| -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ RFC 2821, section 4.4, states that an SMTP server must insert a Return-path: header line into a message when it makes a "final delivery". The Return-path: @@ -14476,29 +14705,30 @@ options for adding Return-path: headers at the time of delivery. They are normally used only for final local deliveries. -+-----------------+---------+-------------+-------------+ ++-------------------------------------------------------+ |return_size_limit|Use: main|Type: integer|Default: 100K| -+-----------------+---------+-------------+-------------+ ++-------------------------------------------------------+ This option is an obsolete synonym for bounce_return_size_limit. -+-------------+---------+----------------+----------+ -|rfc1413_hosts|Use: main|Type: host list*|Default: *| -+-------------+---------+----------------+----------+ ++-----------------------------------------------------+ +|rfc1413_hosts|Use: main|Type: host list*|Default: @[]| ++-----------------------------------------------------+ RFC 1413 identification calls are made to any client host which matches an item -in the list. +in the list. The default value specifies just this host, being any local +interface for the system. -+---------------------+---------+----------+-----------+ -|rfc1413_query_timeout|Use: main|Type: time|Default: 5s| -+---------------------+---------+----------+-----------+ ++------------------------------------------------------+ +|rfc1413_query_timeout|Use: main|Type: time|Default: 0s| ++------------------------------------------------------+ This sets the timeout on RFC 1413 identification calls. If it is set to zero, no RFC 1413 calls are ever made. -+------------------------+---------+----------------+--------------+ ++------------------------------------------------------------------+ |sender_unqualified_hosts|Use: main|Type: host list*|Default: unset| -+------------------------+---------+----------------+--------------+ ++------------------------------------------------------------------+ This option lists those hosts from which Exim is prepared to accept unqualified sender addresses. The addresses are made fully qualified by the addition of @@ -14508,9 +14738,17 @@ sender_unqualified_hosts, or if the message was submitted locally (not using TCP/IP), and the -bnq option was not set. -+---------------------+---------+-------------+-------------+ ++--------------------------------------------------+ +|slow_lookup_log|Use: main|Type: integer|Default: 0| ++--------------------------------------------------+ + +This option controls logging of slow lookups. If the value is nonzero it is +taken as a number of milliseconds and lookups taking longer than this are +logged. Currently this applies only to DNS lookups. + ++-----------------------------------------------------------+ |smtp_accept_keepalive|Use: main|Type: boolean|Default: true| -+---------------------+---------+-------------+-------------+ ++-----------------------------------------------------------+ This option controls the setting of the SO_KEEPALIVE option on incoming TCP/IP socket connections. When set, it causes the kernel to probe idle connections @@ -14522,9 +14760,9 @@ call properly. The keepalive mechanism takes several hours to detect unreachable hosts. -+---------------+---------+-------------+-----------+ ++---------------------------------------------------+ |smtp_accept_max|Use: main|Type: integer|Default: 20| -+---------------+---------+-------------+-----------+ ++---------------------------------------------------+ This option specifies the maximum number of simultaneous incoming SMTP calls that Exim will accept. It applies only to the listening daemon; there is no @@ -14538,9 +14776,9 @@ has not been reached for the client host, smtp_accept_reserve and smtp_load_reserve are then checked before accepting the connection. -+-----------------------+---------+-------------+-----------+ ++-----------------------------------------------------------+ |smtp_accept_max_nonmail|Use: main|Type: integer|Default: 10| -+-----------------------+---------+-------------+-----------+ ++-----------------------------------------------------------+ Exim counts the number of "non-mail" commands in an SMTP session, and drops the connection if there are too many. This option defines "too many". The check @@ -14557,17 +14795,17 @@ counted. Otherwise, all commands other than MAIL, RCPT, DATA, and QUIT are counted. -+-----------------------------+---------+----------------+----------+ ++-------------------------------------------------------------------+ |smtp_accept_max_nonmail_hosts|Use: main|Type: host list*|Default: *| -+-----------------------------+---------+----------------+----------+ ++-------------------------------------------------------------------+ You can control which hosts are subject to the smtp_accept_max_nonmail check by setting this option. The default value makes it apply to all hosts. By changing the value, you can exclude any badly-behaved hosts that you have to live with. -+------------------------------+---------+-------------+-------------+ ++--------------------------------------------------------------------+ |smtp_accept_max_per_connection|Use: main|Type: integer|Default: 1000| -+------------------------------+---------+-------------+-------------+ ++--------------------------------------------------------------------+ The value of this option limits the number of MAIL commands that Exim is prepared to accept over a single SMTP connection, whether or not each command @@ -14576,9 +14814,9 @@ precaution against a client that goes mad (incidents of this type have been seen). -+------------------------+---------+-------------+--------------+ ++---------------------------------------------------------------+ |smtp_accept_max_per_host|Use: main|Type: string*|Default: unset| -+------------------------+---------+-------------+--------------+ ++---------------------------------------------------------------+ This option restricts the number of simultaneous IP connections from a single host (strictly, from a single IP address) to the Exim daemon. The option is @@ -14596,9 +14834,9 @@ could cause a vast number or processes to be created). While the daemon is doing this processing, it cannot accept any other incoming connections. -+-----------------+---------+-------------+----------+ ++----------------------------------------------------+ |smtp_accept_queue|Use: main|Type: integer|Default: 0| -+-----------------+---------+-------------+----------+ ++----------------------------------------------------+ If the number of simultaneous incoming SMTP connections being handled via the listening daemon exceeds this value, messages received by SMTP are just placed @@ -14612,9 +14850,9 @@ queue_only, queue_only_load, queue_smtp_domains, and the various -odx command line options. -+--------------------------------+---------+-------------+-----------+ ++--------------------------------------------------------------------+ |smtp_accept_queue_per_connection|Use: main|Type: integer|Default: 10| -+--------------------------------+---------+-------------+-----------+ ++--------------------------------------------------------------------+ This option limits the number of delivery processes that Exim starts automatically when receiving messages via SMTP, whether via the daemon or by @@ -14626,9 +14864,9 @@ systems. On large systems, the default should probably be increased, and on dial-in client systems it should probably be set to zero (that is, disabled). -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ |smtp_accept_reserve|Use: main|Type: integer|Default: 0| -+-------------------+---------+-------------+----------+ ++------------------------------------------------------+ When smtp_accept_max is set greater than zero, this option specifies a number of SMTP connections that are reserved for connections from the hosts that are @@ -14644,9 +14882,9 @@ accepted only from hosts listed in smtp_reserve_hosts, provided the other criteria for acceptance are met. -+--------------------+---------+-------------+--------------+ ++-----------------------------------------------------------+ |smtp_active_hostname|Use: main|Type: string*|Default: unset| -+--------------------+---------+-------------+--------------+ ++-----------------------------------------------------------+ This option is provided for multi-homed servers that want to masquerade as several different hosts. At the start of an incoming SMTP connection, its value @@ -14671,9 +14909,9 @@ it is also used as the default for HELO commands in callout verification if there is no remote transport from which to obtain a helo_data value. -+-----------+---------+-------------+------------------+ ++------------------------------------------------------+ |smtp_banner|Use: main|Type: string*|Default: see below| -+-----------+---------+-------------+------------------+ ++------------------------------------------------------+ This string, which is expanded every time it is used, is output as the initial positive response to an SMTP connection. The default setting is: @@ -14687,9 +14925,9 @@ in this string. Exim adds it automatically (several times in the case of a multiline response). -+----------------------+---------+-------------+-------------+ ++------------------------------------------------------------+ |smtp_check_spool_space|Use: main|Type: boolean|Default: true| -+----------------------+---------+-------------+-------------+ ++------------------------------------------------------------+ When this option is set, if an incoming SMTP session encounters the SIZE option on a MAIL command, it checks that there is enough space in the spool @@ -14697,9 +14935,9 @@ free the amount specified by check_spool_space (even if that value is zero). If there isn't enough space, a temporary error code is returned. -+--------------------+---------+-------------+-----------+ ++--------------------------------------------------------+ |smtp_connect_backlog|Use: main|Type: integer|Default: 20| -+--------------------+---------+-------------+-----------+ ++--------------------------------------------------------+ This option specifies a maximum number of waiting SMTP connections. Exim passes this value to the TCP/IP system when it sets up its listener. Once this number @@ -14710,9 +14948,9 @@ (to 50, say). It also gives some protection against denial-of-service attacks by SYN flooding. -+-----------------+---------+-------------+-------------+ ++-------------------------------------------------------+ |smtp_enforce_sync|Use: main|Type: boolean|Default: true| -+-----------------+---------+-------------+-------------+ ++-------------------------------------------------------+ The SMTP protocol specification requires the client to wait for a response from the server at certain points in the dialogue. Without PIPELINING these @@ -14730,15 +14968,15 @@ The check can be globally disabled by setting smtp_enforce_sync false. If you want to disable the check selectively (for example, only for certain hosts), you can do so by an appropriate use of a control modifier in an ACL (see -section 42.22). See also pipelining_advertise_hosts. +section 43.22). See also pipelining_advertise_hosts. -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ |smtp_etrn_command|Use: main|Type: string*|Default: unset| -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ If this option is set, the given command is run whenever an SMTP ETRN command is received from a host that is permitted to issue such commands (see chapter -42). The string is split up into separate arguments which are independently +43). The string is split up into separate arguments which are independently expanded. The expansion variable $domain is set to the argument of the ETRN command, and no syntax checking is done on it. For example: @@ -14752,17 +14990,17 @@ SMTP, so it is not possible for it to change the uid before running the command. -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ |smtp_etrn_serialize|Use: main|Type: boolean|Default: true| -+-------------------+---------+-------------+-------------+ ++---------------------------------------------------------+ When this option is set, it prevents the simultaneous execution of more than one identical command as a result of ETRN in an SMTP connection. See section -47.8 for details. +48.8 for details. -+-----------------+---------+-----------------+--------------+ ++------------------------------------------------------------+ |smtp_load_reserve|Use: main|Type: fixed-point|Default: unset| -+-----------------+---------+-----------------+--------------+ ++------------------------------------------------------------+ If the system load average ever gets higher than this, incoming SMTP calls are accepted only from those hosts that match an entry in smtp_reserve_hosts. If @@ -14771,9 +15009,9 @@ on which Exim cannot determine the load average. See also deliver_queue_load_max and queue_only_load. -+-----------------------+---------+-------------+----------+ ++----------------------------------------------------------+ |smtp_max_synprot_errors|Use: main|Type: integer|Default: 3| -+-----------------------+---------+-------------+----------+ ++----------------------------------------------------------+ Exim rejects SMTP commands that contain syntax or protocol errors. In particular, a syntactically invalid email address, as in this command: @@ -14792,18 +15030,18 @@ pipelining_advertise_hosts), and in this situation, "expected" errors do not count towards the limit. -+-------------------------+---------+-------------+----------+ ++------------------------------------------------------------+ |smtp_max_unknown_commands|Use: main|Type: integer|Default: 3| -+-------------------------+---------+-------------+----------+ ++------------------------------------------------------------+ If there are too many unrecognized commands in an incoming SMTP session, an Exim server drops the connection. This is a defence against some kinds of abuse that subvert web clients into making connections to SMTP ports; in these circumstances, a number of non-SMTP command lines are sent first. -+--------------------+---------+----------------+--------------+ ++--------------------------------------------------------------+ |smtp_ratelimit_hosts|Use: main|Type: host list*|Default: unset| -+--------------------+---------+----------------+--------------+ ++--------------------------------------------------------------+ Some sites find it helpful to be able to limit the rate at which certain hosts can send them messages, and the rate at which an individual message can specify @@ -14811,7 +15049,7 @@ Exim has two rate-limiting facilities. This section describes the older facility, which can limit rates within a single connection. The newer ratelimit -ACL condition can limit rates across all connections. See section 42.38 for +ACL condition can limit rates across all connections. See section 43.38 for details of the newer facility. When a host matches smtp_ratelimit_hosts, the values of smtp_ratelimit_mail and @@ -14840,21 +15078,21 @@ increasing by a factor of 1.05 each time. The second setting applies delays to RCPT commands when more than four occur in a single message. -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ |smtp_ratelimit_mail|Use: main|Type: string|Default: unset| -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ See smtp_ratelimit_hosts above. -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ |smtp_ratelimit_rcpt|Use: main|Type: string|Default: unset| -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ See smtp_ratelimit_hosts above. -+--------------------+---------+----------+-----------+ -|smtp_receive_timeout|Use: main|Type: time|Default: 5m| -+--------------------+---------+----------+-----------+ ++------------------------------------------------------+ +|smtp_receive_timeout|Use: main|Type: time*|Default: 5m| ++------------------------------------------------------+ This sets a timeout value for SMTP reception. It applies to all forms of SMTP input, including batch SMTP. If a line of input (either an SMTP command or a @@ -14868,22 +15106,26 @@ The former means that Exim was expecting to read an SMTP command; the latter means that it was in the DATA phase, reading the contents of a message. +If the first character of the option is a "$" the option is expanded before use +and may depend on $sender_host_name, $sender_host_address and $sender_host_port +. + The value set by this option can be overridden by the -os command-line option. A setting of zero time disables the timeout, but this should never be used for SMTP over TCP/IP. (It can be useful in some cases of local input using -bs or -bS.) For non-SMTP input, the reception timeout is controlled by receive_timeout and -or. -+------------------+---------+----------------+--------------+ ++------------------------------------------------------------+ |smtp_reserve_hosts|Use: main|Type: host list*|Default: unset| -+------------------+---------+----------------+--------------+ ++------------------------------------------------------------+ This option defines hosts for which SMTP connections are reserved; see smtp_accept_reserve and smtp_load_reserve above. -+-------------------------+---------+-------------+--------------+ ++----------------------------------------------------------------+ |smtp_return_error_details|Use: main|Type: boolean|Default: false| -+-------------------------+---------+-------------+--------------+ ++----------------------------------------------------------------+ In the default state, Exim uses bland messages such as "Administrative prohibition" when it rejects SMTP commands for policy reasons. Many sysadmins @@ -14896,9 +15138,9 @@ 550-Rejected after DATA: '>' missing at end of address: 550 failing address in "From" header is: > is treated as @@ -14985,9 +15227,9 @@ another MTA, the excess angle brackets are not passed on. If this option is not set, multiple pairs of angle brackets cause a syntax error. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |strip_trailing_dot|Use: main|Type: boolean|Default: false| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set, a trailing dot at the end of a domain in an address is ignored. If this is in the envelope and the message is passed on to another @@ -14995,9 +15237,9 @@ domain causes a syntax error. However, addresses in header lines are checked only when an ACL requests header syntax checking. -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ |syslog_duplication|Use: main|Type: boolean|Default: true| -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ When Exim is logging to syslog, it writes the log lines for its three separate logs at different syslog priorities so that they can in principle be separated @@ -15009,85 +15251,85 @@ is written, at LOG_NOTICE priority. Lines that normally go to both the main and the panic log are written at the LOG_ALERT priority. -+---------------+---------+------------+--------------+ ++-----------------------------------------------------+ |syslog_facility|Use: main|Type: string|Default: unset| -+---------------+---------+------------+--------------+ ++-----------------------------------------------------+ This option sets the syslog "facility" name, used when Exim is logging to syslog. The value must be one of the strings "mail", "user", "news", "uucp", "daemon", or "localx" where x is a digit between 0 and 7. If this option is -unset, "mail" is used. See chapter 51 for details of Exim's logging. +unset, "mail" is used. See chapter 52 for details of Exim's logging. -+------------------+---------+------------+---------------+ ++---------------------------------------------------------+ |syslog_processname|Use: main|Type: string|Default: "exim"| -+------------------+---------+------------+---------------+ ++---------------------------------------------------------+ This option sets the syslog "ident" name, used when Exim is logging to syslog. -The value must be no longer than 32 characters. See chapter 51 for details of +The value must be no longer than 32 characters. See chapter 52 for details of Exim's logging. -+----------------+---------+-------------+-------------+ ++------------------------------------------------------+ |syslog_timestamp|Use: main|Type: boolean|Default: true| -+----------------+---------+-------------+-------------+ ++------------------------------------------------------+ If syslog_timestamp is set false, the timestamps on Exim's log lines are -omitted when these lines are sent to syslog. See chapter 51 for details of +omitted when these lines are sent to syslog. See chapter 52 for details of Exim's logging. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |system_filter|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option specifies an Exim filter file that is applied to all messages at the start of each delivery attempt, before any routing is done. System filters must be Exim filters; they cannot be Sieve filters. If the system filter generates any deliveries to files or pipes, or any new mail messages, the appropriate system_filter_..._transport option(s) must be set, to define which -transports are to be used. Details of this facility are given in chapter 45. +transports are to be used. Details of this facility are given in chapter 46. -+---------------------------------+---------+-------------+--------------+ ++------------------------------------------------------------------------+ |system_filter_directory_transport|Use: main|Type: string*|Default: unset| -+---------------------------------+---------+-------------+--------------+ ++------------------------------------------------------------------------+ This sets the name of the transport driver that is to be used when the save command in a system message filter specifies a path ending in "/", implying delivery of each message into a separate file in some directory. During the delivery, the variable $address_file contains the path name. -+----------------------------+---------+-------------+--------------+ ++-------------------------------------------------------------------+ |system_filter_file_transport|Use: main|Type: string*|Default: unset| -+----------------------------+---------+-------------+--------------+ ++-------------------------------------------------------------------+ This sets the name of the transport driver that is to be used when the save command in a system message filter specifies a path not ending in "/". During the delivery, the variable $address_file contains the path name. -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ |system_filter_group|Use: main|Type: string|Default: unset| -+-------------------+---------+------------+--------------+ ++---------------------------------------------------------+ This option is used only when system_filter_user is also set. It sets the gid under which the system filter is run, overriding any gid that is associated with the user. The value may be numerical or symbolic. -+----------------------------+---------+-------------+--------------+ ++-------------------------------------------------------------------+ |system_filter_pipe_transport|Use: main|Type: string*|Default: unset| -+----------------------------+---------+-------------+--------------+ ++-------------------------------------------------------------------+ This specifies the transport driver that is to be used when a pipe command is used in a system filter. During the delivery, the variable $address_pipe contains the pipe command. -+-----------------------------+---------+-------------+--------------+ ++--------------------------------------------------------------------+ |system_filter_reply_transport|Use: main|Type: string*|Default: unset| -+-----------------------------+---------+-------------+--------------+ ++--------------------------------------------------------------------+ This specifies the transport driver that is to be used when a mail command is used in a system filter. -+------------------+---------+------------+--------------+ ++--------------------------------------------------------+ |system_filter_user|Use: main|Type: string|Default: unset| -+------------------+---------+------------+--------------+ ++--------------------------------------------------------+ If this option is set to root, the system filter is run in the main Exim delivery process, as root. Otherwise, the system filter runs in a separate @@ -15101,9 +15343,9 @@ under which the filter is run is used when transporting them, unless a transport option overrides. -+-----------+---------+-------------+-------------+ ++-------------------------------------------------+ |tcp_nodelay|Use: main|Type: boolean|Default: true| -+-----------+---------+-------------+-------------+ ++-------------------------------------------------+ If this option is set false, it stops the Exim daemon setting the TCP_NODELAY option on its listening sockets. Setting TCP_NODELAY turns off the "Nagle @@ -15114,9 +15356,9 @@ only those sockets that are set up for listening by the daemon. Sockets created by the smtp transport for delivering mail always set TCP_NODELAY. -+--------------------+---------+----------+-----------+ ++-----------------------------------------------------+ |timeout_frozen_after|Use: main|Type: time|Default: 0s| -+--------------------+---------+----------+-----------+ ++-----------------------------------------------------+ If timeout_frozen_after is set to a time greater than zero, a frozen message of any kind that has been on the queue for longer than the given time is @@ -15130,9 +15372,9 @@ messages remain on the queue forever (except for any frozen bounce messages that are released by ignore_bounce_errors_after). -+--------+---------+------------+--------------+ ++----------------------------------------------+ |timezone|Use: main|Type: string|Default: unset| -+--------+---------+------------+--------------+ ++----------------------------------------------+ The value of timezone is used to set the environment variable TZ while running Exim (if it is different on entry). This ensures that all timestamps created by @@ -15148,22 +15390,22 @@ appropriate behaviour for obtaining wall-clock time on some, but unfortunately not all, operating systems. -+-------------------+---------+----------------+--------------+ ++-------------------------------------------------------------+ |tls_advertise_hosts|Use: main|Type: host list*|Default: unset| -+-------------------+---------+----------------+--------------+ ++-------------------------------------------------------------+ When Exim is built with support for TLS encrypted connections, the availability of the STARTTLS command to set up an encrypted session is advertised in response to EHLO only to those client hosts that match this option. See chapter -41 for details of Exim's support for TLS. +42 for details of Exim's support for TLS. -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ |tls_certificate|Use: main|Type: string*|Default: unset| -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ The value of this option is expanded, and must then be the absolute path to a file which contains the server's certificates. The server's private key is also -assumed to be in this file if tls_privatekey is unset. See chapter 41 for +assumed to be in this file if tls_privatekey is unset. See chapter 42 for further details. Note: The certificates defined by this option are used only when Exim is @@ -15173,21 +15415,21 @@ If the option contains $tls_out_sni and Exim is built against OpenSSL, then if the OpenSSL build supports TLS extensions and the TLS client sends the Server -Name Indication extension, then this option and others documented in 41.10 will +Name Indication extension, then this option and others documented in 42.10 will be re-expanded. -+-------+---------+-------------+--------------+ ++----------------------------------------------+ |tls_crl|Use: main|Type: string*|Default: unset| -+-------+---------+-------------+--------------+ ++----------------------------------------------+ This option specifies a certificate revocation list. The expanded value must be the name of a file that contains a CRL in PEM format. -See 41.10 for discussion of when this option might be re-expanded. +See 42.10 for discussion of when this option might be re-expanded. -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ |tls_dh_max_bits|Use: main|Type: integer|Default: 2236| -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ The number of bits used for Diffie-Hellman key-exchange may be suggested by the chosen TLS library. That value might prove to be too high for interoperability. @@ -15207,9 +15449,9 @@ little less than this figure, because GnuTLS is inexact and may produce a larger prime than requested. -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ |tls_dhparam|Use: main|Type: string*|Default: unset| -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ The value of this option is expanded and indicates the source of DH parameters to be used by Exim. @@ -15227,7 +15469,7 @@ If this option expands to the string "historic" and Exim is using GnuTLS, then Exim will attempt to load a file from inside the spool directory. If the file -does not exist, Exim will attempt to create it. See section 41.3 for further +does not exist, Exim will attempt to create it. See section 42.3 for further details. If Exim is using OpenSSL and this option is empty or unset, then Exim will load @@ -15260,47 +15502,60 @@ to the 4.80 release, as Debian used to patch Exim to raise the minimum acceptable bound from 1024 to 2048. -+-------------+---------+-------------+--------------+ ++-------------------------------------------------------+ +|tls_eccurve|Use: main|Type: string*|Default: prime256v1| ++-------------------------------------------------------+ + +If built with a recent-enough version of OpenSSL, this option selects a EC +curve for use by Exim. + +Curve names of the form prime256v1 are accepted. For even more-recent library +versions, names of the form P-512 are also accepted, plus the special value +auto which tell the library to choose. + +If the option is set to an empty string, no EC curves will be enabled. + ++----------------------------------------------------+ |tls_ocsp_file|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This option must if set expand to the absolute path to a file which contains a current status proof for the server's certificate, as obtained from the Certificate Authority. -+--------------------+---------+-----------------+--------------+ ++---------------------------------------------------------------+ |tls_on_connect_ports|Use: main|Type: string list|Default: unset| -+--------------------+---------+-----------------+--------------+ ++---------------------------------------------------------------+ This option specifies a list of incoming SSMTP (aka SMTPS) ports that should operate the obsolete SSMTP (SMTPS) protocol, where a TLS session is immediately set up without waiting for the client to issue a STARTTLS command. For further details, see section 13.4. -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ |tls_privatekey|Use: main|Type: string*|Default: unset| -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ The value of this option is expanded, and must then be the absolute path to a file which contains the server's private key. If this option is unset, or if the expansion is forced to fail, or the result is an empty string, the private key is assumed to be in the same file as the server's certificates. See chapter -41 for further details. +42 for further details. -See 41.10 for discussion of when this option might be re-expanded. +See 42.10 for discussion of when this option might be re-expanded. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |tls_remember_esmtp|Use: main|Type: boolean|Default: false| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set true, Exim violates the RFCs by remembering that it is in "esmtp" state after successfully negotiating a TLS session. This provides support for broken clients that fail to send a new EHLO after starting a TLS session. -+-------------------+---------+-------------+--------------+ ++----------------------------------------------------------+ |tls_require_ciphers|Use: main|Type: string*|Default: unset| -+-------------------+---------+-------------+--------------+ ++----------------------------------------------------------+ This option controls which ciphers can be used for incoming TLS connections. The smtp transport has an option of the same name for controlling outgoing @@ -15308,41 +15563,48 @@ different clients if required. The value of this option must be a list of permitted cipher suites. The OpenSSL and GnuTLS libraries handle cipher control in somewhat different ways. If GnuTLS is being used, the client controls the -preference order of the available ciphers. Details are given in sections 41.4 -and 41.5. +preference order of the available ciphers. Details are given in sections 42.4 +and 42.5. -+--------------------+---------+----------------+--------------+ ++--------------------------------------------------------------+ |tls_try_verify_hosts|Use: main|Type: host list*|Default: unset| -+--------------------+---------+----------------+--------------+ ++--------------------------------------------------------------+ See tls_verify_hosts below. -+-----------------------+---------+-------------+--------------+ -|tls_verify_certificates|Use: main|Type: string*|Default: unset| -+-----------------------+---------+-------------+--------------+ ++---------------------------------------------------------------+ +|tls_verify_certificates|Use: main|Type: string*|Default: system| ++---------------------------------------------------------------+ + +The value of this option is expanded, and must then be either the word "system" +or the absolute path to a file or directory containing permitted certificates +for clients that match tls_verify_hosts or tls_try_verify_hosts. + +The "system" value for the option will use a system default location compiled +into the SSL library. This is not available for GnuTLS versions preceding +3.0.20, and will be taken as empty; an explicit location must be specified. -The value of this option is expanded, and must then be the absolute path to a -file containing permitted certificates for clients that match tls_verify_hosts -or tls_try_verify_hosts. Alternatively, if you are using OpenSSL, you can set -tls_verify_certificates to the name of a directory containing certificate -files. This does not work with GnuTLS; the option must be set to the name of a -single file if you are using GnuTLS. +The use of a directory for the option value is not avilable for GnuTLS versions +preceding 3.3.6 and a single file must be used. + +With OpenSSL the certificates specified explicitly either by file or directory +are added to those given by the system default location. These certificates should be for the certificate authorities trusted, rather than the public cert of individual clients. With both OpenSSL and GnuTLS, if the value is a file then the certificates are sent by Exim as a server to connecting clients, defining the list of accepted certificate authorities. Thus -the values defined should be considered public data. To avoid this, use OpenSSL -with a directory. +the values defined should be considered public data. To avoid this, use the +explicit directory version. -See 41.10 for discussion of when this option might be re-expanded. +See 42.10 for discussion of when this option might be re-expanded. A forced expansion failure or setting to an empty string is equivalent to being unset. -+----------------+---------+----------------+--------------+ ++----------------------------------------------------------+ |tls_verify_hosts|Use: main|Type: host list*|Default: unset| -+----------------+---------+----------------+--------------+ ++----------------------------------------------------------+ This option, along with tls_try_verify_hosts, controls the checking of certificates from clients. The expected certificates are defined by @@ -15369,9 +15631,9 @@ Client hosts that match neither of these lists are not asked to present certificates. -+--------------+---------+------------------+--------------+ ++----------------------------------------------------------+ |trusted_groups|Use: main|Type: string list*|Default: unset| -+--------------+---------+------------------+--------------+ ++----------------------------------------------------------+ This option is expanded just once, at the start of Exim's processing. If this option is set, any process that is running in one of the listed groups, or @@ -15380,9 +15642,9 @@ callers are permitted to do. If neither trusted_groups nor trusted_users is set, only root and the Exim user are trusted. -+-------------+---------+------------------+--------------+ ++---------------------------------------------------------+ |trusted_users|Use: main|Type: string list*|Default: unset| -+-------------+---------+------------------+--------------+ ++---------------------------------------------------------+ This option is expanded just once, at the start of Exim's processing. If this option is set, any process that is running as one of the listed users is @@ -15390,9 +15652,9 @@ details of what trusted callers are permitted to do. If neither trusted_groups nor trusted_users is set, only root and the Exim user are trusted. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |unknown_login|Use: main|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ This is a specialized feature for use in unusual configurations. By default, if the uid of the caller of Exim cannot be looked up using getpwuid(), Exim gives @@ -15401,15 +15663,15 @@ unknown_login is used, the value of unknown_username is used for the user's real name (gecos field), unless this has been set by the -F option. -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ |unknown_username|Use: main|Type: string|Default: unset| -+----------------+---------+------------+--------------+ ++------------------------------------------------------+ See unknown_login. -+--------------------+---------+-------------------+--------------+ ++-----------------------------------------------------------------+ |untrusted_set_sender|Use: main|Type: address list*|Default: unset| -+--------------------+---------+-------------------+--------------+ ++-----------------------------------------------------------------+ When an untrusted user submits a message to Exim using the standard input, Exim normally creates an envelope sender address from the user's login and the @@ -15443,16 +15705,16 @@ parameters. Furthermore, it does not stop Exim from removing an existing Sender: header in the message, or from adding a Sender: header if necessary. See local_sender_retain and local_from_check for ways of overriding these -actions. The handling of the Sender: header is also described in section 46.16. +actions. The handling of the Sender: header is also described in section 47.16. The log line for a message's arrival shows the envelope sender following "<=". For local messages, the user's login always follows, after "U=". In -bp displays, and in the Exim monitor, if an untrusted user sets an envelope sender address, the user's login is shown in parentheses after the sender address. -+-----------------+---------+------------+------------------+ ++-----------------------------------------------------------+ |uucp_from_pattern|Use: main|Type: string|Default: see below| -+-----------------+---------+------------+------------------+ ++-----------------------------------------------------------+ Some applications that pass messages to an MTA via a command line interface use an initial line starting with "From " to pass the envelope sender. In @@ -15475,28 +15737,28 @@ uucp_from_sender is "$1", which therefore just uses this first word ("ph10" in the example above) as the message's sender. See also ignore_fromline_hosts. -+----------------+---------+-------------+-------------+ ++------------------------------------------------------+ |uucp_from_sender|Use: main|Type: string*|Default: "$1"| -+----------------+---------+-------------+-------------+ ++------------------------------------------------------+ See uucp_from_pattern above. -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ |warn_message_file|Use: main|Type: string|Default: unset| -+-----------------+---------+------------+--------------+ ++-------------------------------------------------------+ This option defines a template file containing paragraphs of text to be used for constructing the warning message which is sent by Exim when a message has been on the queue for a specified amount of time, as specified by delay_warning -. Details of the file's contents are given in chapter 48. See also +. Details of the file's contents are given in chapter 49. See also bounce_message_file. -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ |write_rejectlog|Use: main|Type: boolean|Default: true| -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ If this option is set false, Exim no longer writes anything to the reject log. -See chapter 51 for details of what Exim writes to its logs. +See chapter 52 for details of what Exim writes to its logs. @@ -15511,9 +15773,9 @@ of expansion of the options that provide data for a transport is: errors_to, headers_add, headers_remove, transport. -+------------+------------+-------------+--------------+ ++------------------------------------------------------+ |address_data|Use: routers|Type: string*|Default: unset| -+------------+------------+-------------+--------------+ ++------------------------------------------------------+ The string is expanded just before the router is run, that is, after all the precondition tests have succeeded. If the expansion is forced to fail, the @@ -15556,18 +15818,18 @@ ACL, it remains available for use in the rest of the ACL statement. After verifying a sender, the value is transferred to $sender_address_data. -+------------+--------------+-------------+-------------+ ++-------------------------------------------------------+ |address_test|Use: routers**|Type: boolean|Default: true| -+------------+--------------+-------------+-------------+ ++-------------------------------------------------------+ If this option is set false, the router is skipped when routing is being tested by means of the -bt command line option. This can be a convenience when your first router sends messages to an external scanner, because it saves you having to set the "already scanned" indicator when testing real address routing. -+--------------------+------------+-------------+--------------+ ++--------------------------------------------------------------+ |cannot_route_message|Use: routers|Type: string*|Default: unset| -+--------------------+------------+-------------+--------------+ ++--------------------------------------------------------------+ This option specifies a text message that is used when an address cannot be routed because Exim has run out of routers. The default message is "Unrouteable @@ -15588,9 +15850,9 @@ explicitly forced, a message about the failure is written to the main and panic logs, in addition to the normal message about the routing failure. -+------------------+------------+-------------+--------------+ ++------------------------------------------------------------+ |caseful_local_part|Use: routers|Type: boolean|Default: false| -+------------------+------------+-------------+--------------+ ++------------------------------------------------------------+ By default, routers handle the local parts of addresses in a case-insensitive manner, though the actual case is preserved for transmission with the message. @@ -15609,11 +15871,11 @@ This option applies to the processing of an address by a router. When a recipient address is being processed in an ACL, there is a separate control modifier that can be used to specify case-sensitive processing within the ACL -(see section 42.22). +(see section 43.22). -+----------------+--------------+-------------+--------------+ ++------------------------------------------------------------+ |check_local_user|Use: routers**|Type: boolean|Default: false| -+----------------+--------------+-------------+--------------+ ++------------------------------------------------------------+ When this option is true, Exim checks that the local part of the recipient address (with affixes removed if relevant) is the name of an account on the @@ -15637,9 +15899,9 @@ home directory) do not occur when a passwd lookup is used in a local_parts (or any other) precondition. -+---------+--------------+-------------+--------------+ ++-----------------------------------------------------+ |condition|Use: routers**|Type: string*|Default: unset| -+---------+--------------+-------------+--------------+ ++-----------------------------------------------------+ This option specifies a general precondition test that has to succeed for the router to be called. The condition option is the last precondition to be @@ -15674,9 +15936,46 @@ of the other precondition options are common special cases that could in fact be specified using condition. -+-----------+------------+-------------+--------------+ +Historical note: We have condition on ACLs and on Routers. Routers are far +older, and use one set of semantics. ACLs are newer and when they were created, +the ACL condition process was given far stricter parse semantics. The bool{} +expansion condition uses the same rules as ACLs. The bool_lax{} expansion +condition uses the same rules as Routers. More pointedly, the bool_lax{} was +written to match the existing Router rules processing behavior. + +This is best illustrated in an example: + +# If used in an ACL condition will fail with a syntax error, but +# in a router condition any extra characters are treated as a string + +$ exim -be '${if eq {${lc:GOOGLE.com}} {google.com}} {yes} {no}}' +true {yes} {no}} + +$ exim -be '${if eq {${lc:WHOIS.com}} {google.com}} {yes} {no}}' + {yes} {no}} + +In each example above, the if statement actually ends after "{google.com}}". +Since no true or false braces were defined, the default if behavior is to +return a boolean true or a null answer (which evaluates to false). The rest of +the line is then treated as a string. So the first example resulted in the +boolean answer "true" with the string " {yes} {no}}" appended to it. The second +example resulted in the null output (indicating false) with the string " {yes} +{no}}" appended to it. + +In fact you can put excess forward braces in too. In the router condition, +Exim's parser only looks for "{" symbols when they mean something, like after a +"$" or when required as part of a conditional. But otherwise "{" and "}" are +treated as ordinary string characters. + +Thus, in a Router, the above expansion strings will both always evaluate true, +as the result of expansion is a non-empty string which doesn't match an +explicit false value. This can be tricky to debug. By contrast, in an ACL +either of those strings will always result in an expansion error because the +result doesn't look sufficiently boolean. + ++-----------------------------------------------------+ |debug_print|Use: routers|Type: string*|Default: unset| -+-----------+------------+-------------+--------------+ ++-----------------------------------------------------+ If this option is set and debugging is enabled (see the -d command line option) or in address-testing mode (see the -bt command line option), the string is @@ -15690,18 +15989,35 @@ tested. A newline is added to the text if it does not end with one. The variable $router_name contains the name of the router. -+---------------+------------+-------------+--------------+ ++---------------------------------------------------------+ |disable_logging|Use: routers|Type: boolean|Default: false| -+---------------+------------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set true, nothing is logged for any routing errors or for any deliveries caused by this router. You should not set this option unless you really, really know what you are doing. See also the generic transport option of the same name. -+-------+--------------+------------------+--------------+ ++---------------------------------------------------------------------+ +|dnssec_request_domains|Use: routers|Type: domain list*|Default: unset| ++---------------------------------------------------------------------+ + +DNS lookups for domains matching dnssec_request_domains will be done with the +dnssec request bit set. This applies to all of the SRV, MX, AAAA, A lookup +sequence. + ++---------------------------------------------------------------------+ +|dnssec_require_domains|Use: routers|Type: domain list*|Default: unset| ++---------------------------------------------------------------------+ + +DNS lookups for domains matching dnssec_request_domains will be done with the +dnssec request bit set. Any returns not having the Authenticated Data bit (AD +bit) set will be ignored and logged as a host-lookup failure. This applies to +all of the SRV, MX, AAAA, A lookup sequence. + ++--------------------------------------------------------+ |domains|Use: routers**|Type: domain list*|Default: unset| -+-------+--------------+------------------+--------------+ ++--------------------------------------------------------+ If this option is set, the router is skipped unless the current domain matches the list. If the match is achieved by means of a file lookup, the data that the @@ -15709,16 +16025,25 @@ expansions of the driver's private options. See section 3.12 for a list of the order in which preconditions are evaluated. -+------+------------+------------+--------------+ ++-----------------------------------------------+ |driver|Use: routers|Type: string|Default: unset| -+------+------------+------------+--------------+ ++-----------------------------------------------+ This option must always be set. It specifies which of the available routers is to be used. -+---------+------------+-------------+--------------+ ++-----------------------------------------------------+ +|dsn_lasthop|Use: routers|Type: boolean|Default: false| ++-----------------------------------------------------+ + +If this option is set true, and extended DSN (RFC3461) processing is in effect, +Exim will not pass on DSN requests to downstream DSN-aware hosts but will +instead send a success DSN as if the next hop does not support DSN. Not +effective on redirect routers. + ++---------------------------------------------------+ |errors_to|Use: routers|Type: string*|Default: unset| -+---------+------------+-------------+--------------+ ++---------------------------------------------------+ If a router successfully handles an address, it may assign the address to a transport for delivery or it may generate child addresses. In both cases, if @@ -15757,12 +16082,12 @@ return_path. The most common use of errors_to is to direct mailing list bounces to the -manager of the list, as described in section 49.2, or to implement VERP -(Variable Envelope Return Paths) (see section 49.6). +manager of the list, as described in section 50.2, or to implement VERP +(Variable Envelope Return Paths) (see section 50.6). -+----+--------------+-------------+-------------+ ++-----------------------------------------------+ |expn|Use: routers**|Type: boolean|Default: true| -+----+--------------+-------------+-------------+ ++-----------------------------------------------+ If this option is turned off, the router is skipped when testing an address as a result of processing an SMTP EXPN command. You might, for example, want to @@ -15770,34 +16095,34 @@ system alias file. See section 3.12 for a list of the order in which preconditions are evaluated. -The use of the SMTP EXPN command is controlled by an ACL (see chapter 42). When +The use of the SMTP EXPN command is controlled by an ACL (see chapter 43). When Exim is running an EXPN command, it is similar to testing an address with -bt. Compare VRFY, whose counterpart is -bv. -+-----------+------------+-------------+--------------+ ++-----------------------------------------------------+ |fail_verify|Use: routers|Type: boolean|Default: false| -+-----------+------------+-------------+--------------+ ++-----------------------------------------------------+ Setting this option has the effect of setting both fail_verify_sender and fail_verify_recipient to the same value. -+---------------------+------------+-------------+--------------+ ++---------------------------------------------------------------+ |fail_verify_recipient|Use: routers|Type: boolean|Default: false| -+---------------------+------------+-------------+--------------+ ++---------------------------------------------------------------+ If this option is true and an address is accepted by this router when verifying a recipient, verification fails. -+------------------+------------+-------------+--------------+ ++------------------------------------------------------------+ |fail_verify_sender|Use: routers|Type: boolean|Default: false| -+------------------+------------+-------------+--------------+ ++------------------------------------------------------------+ If this option is true and an address is accepted by this router when verifying a sender, verification fails. -+--------------+------------+-----------------+--------------+ ++------------------------------------------------------------+ |fallback_hosts|Use: routers|Type: string list|Default: unset| -+--------------+------------+-----------------+--------------+ ++------------------------------------------------------------+ String expansion is not applied to this option. The argument must be a colon-separated list of host names or IP addresses. The list separator can be @@ -15811,9 +16136,9 @@ randomized for each use. See the fallback_hosts option of the smtp transport for further details. -+-----+------------+-------------+------------------+ ++---------------------------------------------------+ |group|Use: routers|Type: string*|Default: see below| -+-----+------------+-------------+------------------+ ++---------------------------------------------------+ When a router queues an address for a transport, and the transport does not specify a group, the group given here is used when running the delivery @@ -15822,18 +16147,22 @@ check_local_user is set, when the default is taken from the password information. See also initgroups and user and the discussion in chapter 23. -+-----------+------------+-----------+--------------+ ++---------------------------------------------------+ |headers_add|Use: routers|Type: list*|Default: unset| -+-----------+------------+-----------+--------------+ ++---------------------------------------------------+ + +This option specifies a list of text headers, -This option specifies a list of text headers, newline-separated, that is -associated with any addresses that are accepted by the router. Each item is -separately expanded, at routing time. However, this option has no effect when -an address is just being verified. The way in which the text is used to add -header lines at transport time is described in section 46.17. New header lines -are not actually added until the message is in the process of being -transported. This means that references to header lines in string expansions in -the transport's configuration do not "see" the added header lines. +newline-separated (by default, changeable in the usual way), + +that is associated with any addresses that are accepted by the router. Each +item is separately expanded, at routing time. However, this option has no +effect when an address is just being verified. The way in which the text is +used to add header lines at transport time is described in section 47.17. New +header lines are not actually added until the message is in the process of +being transported. This means that references to header lines in string +expansions in the transport's configuration do not "see" the added header +lines. The headers_add option is expanded after errors_to, but before headers_remove and transport. If an item is empty, or if an item expansion is forced to fail, @@ -15855,18 +16184,22 @@ this ambiguous situation should be avoided. The repeat_use option of the redirect router may be of help. -+--------------+------------+-----------+--------------+ ++------------------------------------------------------+ |headers_remove|Use: routers|Type: list*|Default: unset| -+--------------+------------+-----------+--------------+ ++------------------------------------------------------+ + +This option specifies a list of text headers, -This option specifies a list of text headers, colon-separated, that is -associated with any addresses that are accepted by the router. Each item is -separately expanded, at routing time. However, this option has no effect when -an address is just being verified. The way in which the text is used to remove -header lines at transport time is described in section 46.17. Header lines are -not actually removed until the message is in the process of being transported. -This means that references to header lines in string expansions in the -transport's configuration still "see" the original header lines. +colon-separated (by default, changeable in the usual way), + +that is associated with any addresses that are accepted by the router. Each +item is separately expanded, at routing time. However, this option has no +effect when an address is just being verified. The way in which the text is +used to remove header lines at transport time is described in section 47.17. +Header lines are not actually removed until the message is in the process of +being transported. This means that references to header lines in string +expansions in the transport's configuration still "see" the original header +lines. The headers_remove option is expanded after errors_to and headers_add, but before transport. If an item expansion is forced to fail, the item has no @@ -15883,9 +16216,13 @@ this can lead to problems with duplicates -- see the similar warning for headers_add above. -+-------------------+------------+----------------+--------------+ +Warning 3: Because of the separate expansion of the list items, items that +contain a list separator must have it doubled. To avoid this, change the list +separator (6.20). + ++----------------------------------------------------------------+ |ignore_target_hosts|Use: routers|Type: host list*|Default: unset| -+-------------------+------------+----------------+--------------+ ++----------------------------------------------------------------+ Although this option is a host list, it should normally contain IP address entries rather than names. If any host that is looked up by the router has an @@ -15923,9 +16260,9 @@ During its expansion, $host_address is set to the IP address that is being checked. -+----------+------------+-------------+--------------+ ++----------------------------------------------------+ |initgroups|Use: routers|Type: boolean|Default: false| -+----------+------------+-------------+--------------+ ++----------------------------------------------------+ If the router queues an address for a transport, and this option is true, and the uid supplied by the router is not overridden by the transport, the @@ -15933,9 +16270,9 @@ additional groups associated with the uid are set up. See also group and user and the discussion in chapter 23. -+-----------------+--------------+-----------------+--------------+ ++-----------------------------------------------------------------+ |local_part_prefix|Use: routers**|Type: string list|Default: unset| -+-----------------+--------------+-----------------+--------------+ ++-----------------------------------------------------------------+ If this option is set, the router is skipped unless the local part starts with one of the given strings, or local_part_prefix_optional is true. See section @@ -15946,7 +16283,7 @@ asterisk, it matches the longest possible sequence of arbitrary characters at the start of the local part. An asterisk should therefore always be followed by some character that does not occur in normal local parts. Wildcarding can be -used to set up multiple user mailboxes, as described in section 49.8. +used to set up multiple user mailboxes, as described in section 50.8. During the testing of the local_parts option, and while the router is running, the prefix is removed from the local part, and is available in the expansion @@ -15984,15 +16321,15 @@ used in both a prefix and a suffix on the same router. Different separator characters must be used to avoid ambiguity. -+--------------------------+------------+-------------+--------------+ ++--------------------------------------------------------------------+ |local_part_prefix_optional|Use: routers|Type: boolean|Default: false| -+--------------------------+------------+-------------+--------------+ ++--------------------------------------------------------------------+ See local_part_prefix above. -+-----------------+--------------+-----------------+--------------+ ++-----------------------------------------------------------------+ |local_part_suffix|Use: routers**|Type: string list|Default: unset| -+-----------------+--------------+-----------------+--------------+ ++-----------------------------------------------------------------+ This option operates in the same way as local_part_prefix, except that the local part must end (rather than start) with the given string, the @@ -16001,15 +16338,15 @@ suffix. This option facility is commonly used to handle local parts of the form something-request and multiple user mailboxes of the form username-foo. -+--------------------------+------------+-------------+--------------+ ++--------------------------------------------------------------------+ |local_part_suffix_optional|Use: routers|Type: boolean|Default: false| -+--------------------------+------------+-------------+--------------+ ++--------------------------------------------------------------------+ See local_part_suffix above. -+-----------+--------------+----------------------+--------------+ ++----------------------------------------------------------------+ |local_parts|Use: routers**|Type: local part list*|Default: unset| -+-----------+--------------+----------------------+--------------+ ++----------------------------------------------------------------+ The router is run only if the local part of the address matches the list. See section 3.12 for a list of the order in which preconditions are evaluated, and @@ -16030,9 +16367,9 @@ local_parts = postmaster data = postmaster@real.domain.example -+------------+------------+-------------+------------------+ ++----------------------------------------------------------+ |log_as_local|Use: routers|Type: boolean|Default: see below| -+------------+------------+-------------+------------------+ ++----------------------------------------------------------+ Exim has two logging styles for delivery, the idea being to make local deliveries stand out more visibly from remote ones. In the "local" style, the @@ -16042,9 +16379,9 @@ assigns an address to a transport. It has no effect on routers that redirect addresses. -+----+------------+--------------+-------------+ ++----------------------------------------------+ |more|Use: routers|Type: boolean*|Default: true| -+----+------------+--------------+-------------+ ++----------------------------------------------+ The result of string expansion for this option must be a valid boolean value, that is, one of the strings "yes", "no", "true", or "false". Any other result @@ -16067,9 +16404,9 @@ is forced to fail, the router declines, and the value of more controls what happens next. -+---------------+------------+-------------+--------------+ ++---------------------------------------------------------+ |pass_on_timeout|Use: routers|Type: boolean|Default: false| -+---------------+------------+-------------+--------------+ ++---------------------------------------------------------+ If a router times out during a host lookup, it normally causes deferral of the address. If pass_on_timeout is set, the address is passed on to the next @@ -16081,9 +16418,9 @@ lookups. They are treated in the same way as a timeout, and this option applies to all of them. -+-----------+------------+------------+--------------+ ++----------------------------------------------------+ |pass_router|Use: routers|Type: string|Default: unset| -+-----------+------------+------------+--------------+ ++----------------------------------------------------+ Routers that recognize the generic self option (dnslookup, ipliteral, and manualroute) are able to return "pass", forcing routing to continue, and @@ -16095,9 +16432,9 @@ does not apply when a router returns "decline" because it cannot handle an address. -+---------------+------------+------------+--------------+ ++--------------------------------------------------------+ |redirect_router|Use: routers|Type: string|Default: unset| -+---------------+------------+------------+--------------+ ++--------------------------------------------------------+ Sometimes an administrator knows that it is pointless to reprocess addresses generated from alias or forward files with the same router again. For example, @@ -16109,9 +16446,9 @@ instead of at the first router. This option has no effect if the router in which it is set does not generate new addresses. -+-------------+--------------+------------------+--------------+ ++--------------------------------------------------------------+ |require_files|Use: routers**|Type: string list*|Default: unset| -+-------------+--------------+------------------+--------------+ ++--------------------------------------------------------------+ This option provides a general mechanism for predicating the running of a router on the existence or non-existence of certain files or directories. @@ -16189,9 +16526,9 @@ users' .forward files), another solution is to set the verify option false so that the router is skipped when verifying. -+--------------------+------------+-------------+------------------+ ++------------------------------------------------------------------+ |retry_use_local_part|Use: routers|Type: boolean|Default: see below| -+--------------------+------------+-------------+------------------+ ++------------------------------------------------------------------+ When a delivery suffers a temporary routing failure, a retry record is created in Exim's hints database. For addresses whose routing depends only on the @@ -16211,9 +16548,9 @@ appears. If the router generates child addresses, they are routed independently; this setting does not become attached to them. -+---------------------+------------+-------------+--------------+ ++---------------------------------------------------------------+ |router_home_directory|Use: routers|Type: string*|Default: unset| -+---------------------+------------+-------------+--------------+ ++---------------------------------------------------------------+ This option sets a home directory for use while the router is running. (Compare transport_home_directory, which sets a home directory for later transporting.) @@ -16243,9 +16580,9 @@ In other words, router_home_directory overrides the password data for the router, but not for the transport. -+----+------------+------------+---------------+ ++----------------------------------------------+ |self|Use: routers|Type: string|Default: freeze| -+----+------------+------------+---------------+ ++----------------------------------------------+ This option applies to those routers that use a recipient address to find a list of remote hosts. Currently, these are the dnslookup, ipliteral, and @@ -16308,9 +16645,9 @@ Exim with a different configuration file that handles the domain in another way. -+-------+--------------+-------------------+--------------+ ++---------------------------------------------------------+ |senders|Use: routers**|Type: address list*|Default: unset| -+-------+--------------+-------------------+--------------+ ++---------------------------------------------------------+ If this option is set, the router is skipped unless the message's sender address matches something on the list. See section 3.12 for a list of the order @@ -16324,9 +16661,9 @@ sender, but is available when verifying any recipients. If the SMTP VRFY command is enabled, it must be used after MAIL if the sender address matters. -+--------------------+------------+-------------+--------------+ ++--------------------------------------------------------------+ |translate_ip_address|Use: routers|Type: string*|Default: unset| -+--------------------+------------+-------------+--------------+ ++--------------------------------------------------------------+ There exist some rare networking situations (for example, packet radio) where it is helpful to be able to translate IP addresses generated by normal routing @@ -16358,9 +16695,9 @@ You should not make use of this facility unless you really understand what you are doing. -+---------+------------+-------------+--------------+ ++---------------------------------------------------+ |transport|Use: routers|Type: string*|Default: unset| -+---------+------------+-------------+--------------+ ++---------------------------------------------------+ This option specifies the transport to be used when a router accepts an address and sets it up for delivery. A transport is never needed if a router is used @@ -16373,9 +16710,9 @@ private options that set up transports for pipe and file deliveries (see chapter 22). -+---------------------------+------------+-------------+--------------+ ++---------------------------------------------------------------------+ |transport_current_directory|Use: routers|Type: string*|Default: unset| -+---------------------------+------------+-------------+--------------+ ++---------------------------------------------------------------------+ This option associates a current directory with any address that is routed to a local transport. This can happen either because a transport is explicitly @@ -16386,9 +16723,9 @@ forced failure, an error is logged, and delivery is deferred. See chapter 23 for details of the local delivery environment. -+------------------------+------------+-------------+------------------+ ++----------------------------------------------------------------------+ |transport_home_directory|Use: routers|Type: string*|Default: see below| -+------------------------+------------+-------------+------------------+ ++----------------------------------------------------------------------+ This option associates a home directory with any address that is routed to a local transport. This can happen either because a transport is explicitly @@ -16406,9 +16743,9 @@ See chapter 23 for further details of the local delivery environment. -+------+------------+--------------+--------------+ ++-------------------------------------------------+ |unseen|Use: routers|Type: boolean*|Default: false| -+------+------------+--------------+--------------+ ++-------------------------------------------------+ The result of string expansion for this option must be a valid boolean value, that is, one of the strings "yes", "no", "true", or "false". Any other result @@ -16446,9 +16783,9 @@ address_data option in the current or previous routers is passed on to subsequent routers. -+----+------------+-------------+------------------+ ++--------------------------------------------------+ |user|Use: routers|Type: string*|Default: see below| -+----+------------+-------------+------------------+ ++--------------------------------------------------+ When a router queues an address for a transport, and the transport does not specify a user, the user given here is used when running the delivery process. @@ -16460,16 +16797,16 @@ group associated with the user is used. See also initgroups and group and the discussion in chapter 23. -+------+--------------+-------------+-------------+ ++-------------------------------------------------+ |verify|Use: routers**|Type: boolean|Default: true| -+------+--------------+-------------+-------------+ ++-------------------------------------------------+ Setting this option has the effect of setting verify_sender and verify_recipient to the same value. -+-----------+--------------+-------------+--------------+ ++-------------------------------------------------------+ |verify_only|Use: routers**|Type: boolean|Default: false| -+-----------+--------------+-------------+--------------+ ++-------------------------------------------------------+ If this option is set, the router is used only when verifying an address, delivering in cutthrough mode or testing with the -bv option, not when actually @@ -16482,22 +16819,22 @@ accesses any files, you need to make sure that they are accessible to the Exim user or group. -+----------------+--------------+-------------+-------------+ ++-----------------------------------------------------------+ |verify_recipient|Use: routers**|Type: boolean|Default: true| -+----------------+--------------+-------------+-------------+ ++-----------------------------------------------------------+ If this option is false, the router is skipped when verifying recipient addresses, delivering in cutthrough mode or testing recipient verification using -bv. See section 3.12 for a list of the order in which preconditions are -evaluated. +evaluated. See also the $verify_mode variable. -+-------------+--------------+-------------+-------------+ ++--------------------------------------------------------+ |verify_sender|Use: routers**|Type: boolean|Default: true| -+-------------+--------------+-------------+-------------+ ++--------------------------------------------------------+ If this option is false, the router is skipped when verifying sender addresses or testing sender verification using -bvs. See section 3.12 for a list of the -order in which preconditions are evaluated. +order in which preconditions are evaluated. See also the $verify_mode variable. @@ -16578,6 +16915,9 @@ address; if such a router is expected to handle "all remaining non-local domains", then it is important to set no_more. +The router will defer rather than decline if the domain is found in the +fail_defer_domains router option. + Reasons for a dnslookup router to decline currently include: * The domain does not exist in DNS @@ -16608,9 +16948,9 @@ The private options for the dnslookup router are as follows: -+------------------+--------------+-------------+--------------+ ++--------------------------------------------------------------+ |check_secondary_mx|Use: dnslookup|Type: boolean|Default: false| -+------------------+--------------+-------------+--------------+ ++--------------------------------------------------------------+ If this option is set, the router declines unless the local host is found in (and removed from) the list of hosts obtained by MX lookup. This can be used to @@ -16618,9 +16958,9 @@ differently to other domains. The way in which Exim decides whether a host is the local host is described in section 13.8. -+---------+--------------+-------------+--------------+ ++-----------------------------------------------------+ |check_srv|Use: dnslookup|Type: string*|Default: unset| -+---------+--------------+-------------+--------------+ ++-----------------------------------------------------+ The dnslookup router supports the use of SRV records (see RFC 2782) in addition to MX and address records. The support is disabled by default. To enable SRV @@ -16654,26 +16994,19 @@ See section 17.1 above for a discussion of Exim's behaviour when there is a DNS lookup error. -+----------------------+--------------+------------------+--------------+ -|dnssec_request_domains|Use: dnslookup|Type: domain list*|Default: unset| -+----------------------+--------------+------------------+--------------+ - -DNS lookups for domains matching dnssec_request_domains will be done with the -dnssec request bit set. This applies to all of the SRV, MX A6, AAAA, A lookup -sequence. - -+----------------------+--------------+------------------+--------------+ -|dnssec_require_domains|Use: dnslookup|Type: domain list*|Default: unset| -+----------------------+--------------+------------------+--------------+ ++-------------------------------------------------------------------+ +|fail_defer_domains|Use: dnslookup|Type: domain list*|Default: unset| ++-------------------------------------------------------------------+ + +DNS lookups for domains matching fail_defer_domains which find no matching +record will cause the router to defer rather than the default behaviour of +decline. This maybe be useful for queueing messages for a newly created domain +while the DNS configuration is not ready. However, it will result in any +message with mistyped domains also being queued. -DNS lookups for domains matching dnssec_request_domains will be done with the -dnssec request bit set. Any returns not having the Authenticated Data bit (AD -bit) set wil be ignored and logged as a host-lookup failure. This applies to -all of the SRV, MX A6, AAAA, A lookup sequence. - -+----------+--------------+------------------+--------------+ ++-----------------------------------------------------------+ |mx_domains|Use: dnslookup|Type: domain list*|Default: unset| -+----------+--------------+------------------+--------------+ ++-----------------------------------------------------------+ A domain that matches mx_domains is required to have either an MX or an SRV record in order to be recognized. (The name of this option could be improved.) @@ -16687,17 +17020,17 @@ has no MX record should be bounced immediately instead of being routed using the address record. -+---------------+--------------+------------------+--------------+ ++----------------------------------------------------------------+ |mx_fail_domains|Use: dnslookup|Type: domain list*|Default: unset| -+---------------+--------------+------------------+--------------+ ++----------------------------------------------------------------+ If the DNS lookup for MX records for one of the domains in this list causes a DNS lookup error, Exim behaves as if no MX records were found. See section 17.1 for more discussion. -+--------------+--------------+-------------+-------------+ ++---------------------------------------------------------+ |qualify_single|Use: dnslookup|Type: boolean|Default: true| -+--------------+--------------+-------------+-------------+ ++---------------------------------------------------------+ When this option is true, the resolver option RES_DEFNAMES is set for DNS lookups. Typically, but not standardly, this causes the resolver to qualify @@ -16706,9 +17039,9 @@ thesaurus.ref.example inside the resolver. For details of what your resolver actually does, consult your man pages for resolver and resolv.conf. -+---------------+--------------+-------------+-------------+ ++----------------------------------------------------------+ |rewrite_headers|Use: dnslookup|Type: boolean|Default: true| -+---------------+--------------+-------------+-------------+ ++----------------------------------------------------------+ If the domain name in the address that is being processed is not fully qualified, it may be expanded to its full form by a DNS lookup. For example, if @@ -16729,9 +17062,9 @@ name returned by a DNS lookup begins with an asterisk, it is not used for header rewriting. -+------------------------+--------------+-------------+--------------+ ++--------------------------------------------------------------------+ |same_domain_copy_routing|Use: dnslookup|Type: boolean|Default: false| -+------------------------+--------------+-------------+--------------+ ++--------------------------------------------------------------------+ Addresses with the same domain are normally routed by the dnslookup router to the same list of hosts. However, this cannot be presumed, because the router @@ -16754,9 +17087,9 @@ * The router did not change the address in any way, for example, by "widening" the domain. -+--------------+--------------+-------------+--------------+ ++----------------------------------------------------------+ |search_parents|Use: dnslookup|Type: boolean|Default: false| -+--------------+--------------+-------------+--------------+ ++----------------------------------------------------------+ When this option is true, the resolver option RES_DNSRCH is set for DNS lookups. This is different from the qualify_single option in that it applies to @@ -16771,17 +17104,17 @@ record, because any domain that does not have its own MX record matches the local wildcard. -+----------------+--------------+------------------+--------------+ ++-----------------------------------------------------------------+ |srv_fail_domains|Use: dnslookup|Type: domain list*|Default: unset| -+----------------+--------------+------------------+--------------+ ++-----------------------------------------------------------------+ If the DNS lookup for SRV records for one of the domains in this list causes a DNS lookup error, Exim behaves as if no SRV records were found. See section 17.1 for more discussion. -+-------------+--------------+-----------------+--------------+ ++-------------------------------------------------------------+ |widen_domains|Use: dnslookup|Type: string list|Default: unset| -+-------------+--------------+-----------------+--------------+ ++-------------------------------------------------------------+ If a DNS lookup fails and this option is set, each of its strings in turn is added onto the end of the domain, and the lookup is tried again. For example, @@ -16867,40 +17200,40 @@ can be deferred. Since iplookup is just a rewriting router, a transport must not be specified for it. -+-----+-------------+------------+--------------+ ++-----------------------------------------------+ |hosts|Use: iplookup|Type: string|Default: unset| -+-----+-------------+------------+--------------+ ++-----------------------------------------------+ This option must be supplied. Its value is a colon-separated list of host names. The hosts are looked up using gethostbyname() (or getipnodebyname() when available) and are tried in order until one responds to the query. If none respond, what happens is controlled by optional. -+--------+-------------+-------------+--------------+ ++---------------------------------------------------+ |optional|Use: iplookup|Type: boolean|Default: false| -+--------+-------------+-------------+--------------+ ++---------------------------------------------------+ If optional is true, if no response is obtained from any host, the address is passed to the next router, overriding no_more. If optional is false, delivery to the address is deferred. -+----+-------------+-------------+----------+ ++-------------------------------------------+ |port|Use: iplookup|Type: integer|Default: 0| -+----+-------------+-------------+----------+ ++-------------------------------------------+ This option must be supplied. It specifies the port number for the TCP or UDP call. -+--------+-------------+------------+------------+ ++------------------------------------------------+ |protocol|Use: iplookup|Type: string|Default: udp| -+--------+-------------+------------+------------+ ++------------------------------------------------+ This option can be set to "udp" or "tcp" to specify which of the two protocols is to be used. -+-----+-------------+-------------+------------------+ ++----------------------------------------------------+ |query|Use: iplookup|Type: string*|Default: see below| -+-----+-------------+-------------+------------------+ ++----------------------------------------------------+ This defines the content of the query that is sent to the remote hosts. The default value is: @@ -16910,9 +17243,9 @@ The repetition serves as a way of checking that a response is to the correct query in the default case (see response_pattern below). -+-------+-------------+-------------+--------------+ ++--------------------------------------------------+ |reroute|Use: iplookup|Type: string*|Default: unset| -+-------+-------------+-------------+--------------+ ++--------------------------------------------------+ If this option is not set, the rerouted address is precisely the byte string returned by the remote host, up to the first white space, if any. If set, the @@ -16922,9 +17255,9 @@ pattern is in use. In all cases, the rerouted address must end up in the form local_part@domain. -+----------------+-------------+------------+--------------+ ++----------------------------------------------------------+ |response_pattern|Use: iplookup|Type: string|Default: unset| -+----------------+-------------+------------+--------------+ ++----------------------------------------------------------+ This option can be set to a regular expression that is applied to the string returned from the remote host. If the pattern does not match the response, the @@ -16937,9 +17270,9 @@ response_pattern = ^([^@]+)$ reroute = $local_part@$1 -+-------+-------------+----------+-----------+ ++--------------------------------------------+ |timeout|Use: iplookup|Type: time|Default: 5s| -+-------+-------------+----------+-----------+ ++--------------------------------------------+ This specifies the amount of time to wait for a response from the remote machine. The same timeout is used for the connect() function for a TCP call. It @@ -16985,15 +17318,15 @@ The private options for the manualroute router are as follows: -+----------------+----------------+------------+--------------+ ++-------------------------------------------------------------+ |host_all_ignored|Use: manualroute|Type: string|Default: defer| -+----------------+----------------+------------+--------------+ ++-------------------------------------------------------------+ See host_find_failed. -+----------------+----------------+------------+---------------+ ++--------------------------------------------------------------+ |host_find_failed|Use: manualroute|Type: string|Default: freeze| -+----------------+----------------+------------+---------------+ ++--------------------------------------------------------------+ This option controls what happens when manualroute tries to find an IP address for a host, and the host does not exist. The option can be set to one of the @@ -17021,9 +17354,9 @@ if a host lookup gets a temporary error, delivery is deferred unless the generic pass_on_timeout option is set. -+---------------+----------------+-------------+--------------+ ++-------------------------------------------------------------+ |hosts_randomize|Use: manualroute|Type: boolean|Default: false| -+---------------+----------------+-------------+--------------+ ++-------------------------------------------------------------+ If this option is set, the order of the items in a host list in a routing rule is randomized each time the list is used, unless an option in the routing rule @@ -17046,9 +17379,9 @@ randomized host list is passed to an smtp transport that also has hosts_randomize set, the list is not re-randomized. -+----------+----------------+-------------+--------------+ ++--------------------------------------------------------+ |route_data|Use: manualroute|Type: string*|Default: unset| -+----------+----------------+-------------+--------------+ ++--------------------------------------------------------+ If this option is set, it must expand to yield the data part of a routing rule. Typically, the expansion string includes a lookup based on the domain. For @@ -17060,17 +17393,17 @@ router declines. Other kinds of expansion failure cause delivery to be deferred. -+----------+----------------+-----------------+--------------+ ++------------------------------------------------------------+ |route_list|Use: manualroute|Type: string list|Default: unset| -+----------+----------------+-----------------+--------------+ ++------------------------------------------------------------+ This string is a list of routing rules, in the form defined below. Note that, unlike most string lists, the items are separated by semicolons. This is so that they may contain colon-separated host lists. -+------------------------+----------------+-------------+--------------+ ++----------------------------------------------------------------------+ |same_domain_copy_routing|Use: manualroute|Type: boolean|Default: false| -+------------------------+----------------+-------------+--------------+ ++----------------------------------------------------------------------+ Addresses with the same domain are normally routed by the manualroute router to the same list of hosts. However, this cannot be presumed, because the router @@ -17444,26 +17777,26 @@ skip this router for most addresses, it could sensibly be used in special cases, even on a busy host. There are the following private options: -+-------+-----------------+-------------+--------------+ ++------------------------------------------------------+ |command|Use: queryprogram|Type: string*|Default: unset| -+-------+-----------------+-------------+--------------+ ++------------------------------------------------------+ This option must be set. It specifies the command that is to be run. The command is split up into a command name and arguments, and then each is expanded separately (exactly as for a pipe transport, described in chapter 29). -+-------------+-----------------+------------+--------------+ ++-----------------------------------------------------------+ |command_group|Use: queryprogram|Type: string|Default: unset| -+-------------+-----------------+------------+--------------+ ++-----------------------------------------------------------+ This option specifies a gid to be set when running the command while routing an address for deliver. It must be set if command_user specifies a numerical uid. If it begins with a digit, it is interpreted as the numerical value of the gid. Otherwise it is looked up using getgrnam(). -+------------+-----------------+------------+--------------+ ++----------------------------------------------------------+ |command_user|Use: queryprogram|Type: string|Default: unset| -+------------+-----------------+------------+--------------+ ++----------------------------------------------------------+ This option must be set. It specifies the uid which is set when running the command while routing an address for delivery. If the value begins with a @@ -17479,16 +17812,16 @@ the command. In this circumstance the command runs under the current uid and gid. -+-----------------+-----------------+------------+----------+ ++-----------------------------------------------------------+ |current_directory|Use: queryprogram|Type: string|Default: /| -+-----------------+-----------------+------------+----------+ ++-----------------------------------------------------------+ This option specifies an absolute path which is made the current directory before running the command. -+-------+-----------------+----------+-----------+ ++------------------------------------------------+ |timeout|Use: queryprogram|Type: time|Default: 1h| -+-------+-----------------+----------+-----------+ ++------------------------------------------------+ If the command does not complete within the timeout period, its process group is killed and the message is frozen. A value of zero time specifies no timeout. @@ -17585,6 +17918,9 @@ and pipes, and for generating autoreplies. See the file_transport, pipe_transport and reply_transport descriptions below. +If success DSNs have been requested redirection triggers one and the DSN +options are not passed any further. + 22.1 Redirection data --------------------- @@ -17933,23 +18269,23 @@ The private options for the redirect router are as follows: -+-----------+-------------+-------------+--------------+ ++------------------------------------------------------+ |allow_defer|Use: redirect|Type: boolean|Default: false| -+-----------+-------------+-------------+--------------+ ++------------------------------------------------------+ Setting this option allows the use of :defer: in non-filter redirection data, or the defer command in an Exim filter file. -+----------+-------------+-------------+--------------+ ++-----------------------------------------------------+ |allow_fail|Use: redirect|Type: boolean|Default: false| -+----------+-------------+-------------+--------------+ ++-----------------------------------------------------+ If this option is true, the :fail: item can be used in a redirection list, and the fail command may be used in an Exim filter file. -+------------+-------------+-------------+--------------+ ++-------------------------------------------------------+ |allow_filter|Use: redirect|Type: boolean|Default: false| -+------------+-------------+-------------+--------------+ ++-------------------------------------------------------+ Setting this option allows Exim to interpret redirection data that starts with "#Exim filter" or "#Sieve filter" as a set of filtering instructions. There are @@ -17965,18 +18301,18 @@ run as the relevant user. When allow_filter is set true, Exim insists that either check_local_user or user is set. -+------------+-------------+-------------+--------------+ ++-------------------------------------------------------+ |allow_freeze|Use: redirect|Type: boolean|Default: false| -+------------+-------------+-------------+--------------+ ++-------------------------------------------------------+ Setting this option allows the use of the freeze command in an Exim filter. This command is more normally encountered in system filters, and is disabled by default for redirection filters because it isn't something you usually want to let ordinary users do. -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ |check_ancestor|Use: redirect|Type: boolean|Default: false| -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ This option is concerned with handling generated addresses that are the same as some address in the list of redirection ancestors of the current address. @@ -18001,9 +18337,9 @@ the .forward file prevents it from turning "jb" back into "joe.bloggs" when that was the original address. See also the repeat_use option below. -+-----------+-------------+-------------+------------------+ ++----------------------------------------------------------+ |check_group|Use: redirect|Type: boolean|Default: see below| -+-----------+-------------+-------------+------------------+ ++----------------------------------------------------------+ When the file option is used, the group owner of the file is checked only when this option is set. The permitted groups are those listed in the owngroups @@ -18013,9 +18349,9 @@ group write bit, or if the owngroups option is set. Otherwise it is false, and no group check occurs. -+-----------+-------------+-------------+------------------+ ++----------------------------------------------------------+ |check_owner|Use: redirect|Type: boolean|Default: see below| -+-----------+-------------+-------------+------------------+ ++----------------------------------------------------------+ When the file option is used, the owner of the file is checked only when this option is set. If check_local_user is set, the local user is permitted; @@ -18023,9 +18359,9 @@ default value for this option is true if check_local_user or owners is set. Otherwise the default is false, and no owner check occurs. -+----+-------------+-------------+--------------+ ++-----------------------------------------------+ |data|Use: redirect|Type: string*|Default: unset| -+----+-------------+-------------+--------------+ ++-----------------------------------------------+ This option is mutually exclusive with file. One or other of them must be set, but not both. The contents of data are expanded, and then used as the list of @@ -18044,18 +18380,18 @@ you can use the ${sg} expansion item to turn the escape string of your choice into a newline. -+-------------------+-------------+-------------+--------------+ ++--------------------------------------------------------------+ |directory_transport|Use: redirect|Type: string*|Default: unset| -+-------------------+-------------+-------------+--------------+ ++--------------------------------------------------------------+ A redirect router sets up a direct delivery to a directory when a path name ending with a slash is specified as a new "address". The transport used is specified by this option, which, after expansion, must be the name of a configured transport. This should normally be an appendfile transport. -+----+-------------+-------------+--------------+ ++-----------------------------------------------+ |file|Use: redirect|Type: string*|Default: unset| -+----+-------------+-------------+--------------+ ++-----------------------------------------------+ This option specifies the name of a file that contains the redirection data. It is mutually exclusive with the data option. The string is expanded before use; @@ -18072,9 +18408,9 @@ a mount problem. If the containing directory does exist, but the file does not, the router declines. -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ |file_transport|Use: redirect|Type: string*|Default: unset| -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ A redirect router sets up a direct delivery to a file when a path name not ending in a slash is specified as a new "address". The transport used is @@ -18082,32 +18418,32 @@ configured transport. This should normally be an appendfile transport. When it is running, the file name is in $address_file. -+-------------------+-------------+-------------+-------------+ ++-------------------------------------------------------------+ |filter_prepend_home|Use: redirect|Type: boolean|Default: true| -+-------------------+-------------+-------------+-------------+ ++-------------------------------------------------------------+ When this option is true, if a save command in an Exim filter specifies a relative path, and $home is defined, it is automatically prepended to the relative path. If this option is set false, this action does not happen. The relative path is then passed to the transport unmodified. -+----------------+-------------+-------------+--------------+ ++-----------------------------------------------------------+ |forbid_blackhole|Use: redirect|Type: boolean|Default: false| -+----------------+-------------+-------------+--------------+ ++-----------------------------------------------------------+ If this option is true, the :blackhole: item may not appear in a redirection list. -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ |forbid_exim_filter|Use: redirect|Type: boolean|Default: false| -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ If this option is set true, only Sieve filters are permitted when allow_filter is true. -+-----------+-------------+-------------+--------------+ ++------------------------------------------------------+ |forbid_file|Use: redirect|Type: boolean|Default: false| -+-----------+-------------+-------------+--------------+ ++------------------------------------------------------+ If this option is true, this router may not generate a new address that specifies delivery to a local file or directory, either from a filter or from a @@ -18115,77 +18451,77 @@ It applies to Sieve filters as well as to Exim filters, but if true, it locks out the Sieve's "keep" facility. -+--------------------+-------------+-------------+--------------+ ++---------------------------------------------------------------+ |forbid_filter_dlfunc|Use: redirect|Type: boolean|Default: false| -+--------------------+-------------+-------------+--------------+ ++---------------------------------------------------------------+ If this option is true, string expansions in Exim filters are not allowed to make use of the dlfunc expansion facility to run dynamically loaded functions. -+------------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------------+ |forbid_filter_existstest|Use: redirect|Type: boolean|Default: false| -+------------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------------+ If this option is true, string expansions in Exim filters are not allowed to make use of the exists condition or the stat expansion item. -+----------------------+-------------+-------------+--------------+ ++-----------------------------------------------------------------+ |forbid_filter_logwrite|Use: redirect|Type: boolean|Default: false| -+----------------------+-------------+-------------+--------------+ ++-----------------------------------------------------------------+ If this option is true, use of the logging facility in Exim filters is not permitted. Logging is in any case available only if the filter is being run under some unprivileged uid (which is normally the case for ordinary users' .forward files). -+--------------------+-------------+-------------+--------------+ ++---------------------------------------------------------------+ |forbid_filter_lookup|Use: redirect|Type: boolean|Default: false| -+--------------------+-------------+-------------+--------------+ ++---------------------------------------------------------------+ If this option is true, string expansions in Exim filter files are not allowed to make use of lookup items. -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ |forbid_filter_perl|Use: redirect|Type: boolean|Default: false| -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ This option has an effect only if Exim is built with embedded Perl support. If it is true, string expansions in Exim filter files are not allowed to make use of the embedded Perl support. -+----------------------+-------------+-------------+--------------+ ++-----------------------------------------------------------------+ |forbid_filter_readfile|Use: redirect|Type: boolean|Default: false| -+----------------------+-------------+-------------+--------------+ ++-----------------------------------------------------------------+ If this option is true, string expansions in Exim filter files are not allowed to make use of readfile items. -+------------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------------+ |forbid_filter_readsocket|Use: redirect|Type: boolean|Default: false| -+------------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------------+ If this option is true, string expansions in Exim filter files are not allowed to make use of readsocket items. -+-------------------+-------------+-------------+--------------+ ++--------------------------------------------------------------+ |forbid_filter_reply|Use: redirect|Type: boolean|Default: false| -+-------------------+-------------+-------------+--------------+ ++--------------------------------------------------------------+ If this option is true, this router may not generate an automatic reply message. Automatic replies can be generated only from Exim or Sieve filter files, not from traditional forward files. This option is forced to be true if one_time is set. -+-----------------+-------------+-------------+--------------+ ++------------------------------------------------------------+ |forbid_filter_run|Use: redirect|Type: boolean|Default: false| -+-----------------+-------------+-------------+--------------+ ++------------------------------------------------------------+ If this option is true, string expansions in Exim filter files are not allowed to make use of run items. -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ |forbid_include|Use: redirect|Type: boolean|Default: false| -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ If this option is true, items of the form @@ -18193,32 +18529,32 @@ are not permitted in non-filter redirection lists. -+-----------+-------------+-------------+--------------+ ++------------------------------------------------------+ |forbid_pipe|Use: redirect|Type: boolean|Default: false| -+-----------+-------------+-------------+--------------+ ++------------------------------------------------------+ If this option is true, this router may not generate a new address which specifies delivery to a pipe, either from an Exim filter or from a conventional forward file. This option is forced to be true if one_time is set. -+-------------------+-------------+-------------+--------------+ ++--------------------------------------------------------------+ |forbid_sieve_filter|Use: redirect|Type: boolean|Default: false| -+-------------------+-------------+-------------+--------------+ ++--------------------------------------------------------------+ If this option is set true, only Exim filters are permitted when allow_filter is true. -+----------------+-------------+-------------+--------------+ ++-----------------------------------------------------------+ |forbid_smtp_code|Use: redirect|Type: boolean|Default: false| -+----------------+-------------+-------------+--------------+ ++-----------------------------------------------------------+ If this option is set true, any SMTP error codes that are present at the start of messages specified for ":defer:" or ":fail:" are quietly ignored, and the default codes (451 and 550, respectively) are always used. -+--------------------+-------------+-------------+--------------+ ++---------------------------------------------------------------+ |hide_child_in_errmsg|Use: redirect|Type: boolean|Default: false| -+--------------------+-------------+-------------+--------------+ ++---------------------------------------------------------------+ If this option is true, it prevents Exim from quoting a child address if it generates a bounce or delay message for it. Instead it says "an address @@ -18226,17 +18562,17 @@ bounces generated locally. If a message is forwarded to another host, its bounce may well quote the generated address. -+-------------+-------------+-------------+--------------+ ++--------------------------------------------------------+ |ignore_eacces|Use: redirect|Type: boolean|Default: false| -+-------------+-------------+-------------+--------------+ ++--------------------------------------------------------+ If this option is set and an attempt to open a redirection file yields the EACCES error (permission denied), the redirect router behaves as if the file did not exist. -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ |ignore_enotdir|Use: redirect|Type: boolean|Default: false| -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set and an attempt to open a redirection file yields the ENOTDIR error (something on the path is not a directory), the redirect router @@ -18251,23 +18587,23 @@ (the ENOTDIR error). This is a confusing area, because it seems that some operating systems give ENOENT where others give ENOTDIR. -+-----------------+-------------+------------+--------------+ ++-----------------------------------------------------------+ |include_directory|Use: redirect|Type: string|Default: unset| -+-----------------+-------------+------------+--------------+ ++-----------------------------------------------------------+ If this option is set, the path names of any :include: items in a redirection list must start with this directory. -+--------+-------------+-------------------+------------+ ++-------------------------------------------------------+ |modemask|Use: redirect|Type: octal integer|Default: 022| -+--------+-------------+-------------------+------------+ ++-------------------------------------------------------+ This specifies mode bits which must not be set for a file specified by the file option. If any of the forbidden bits are set, delivery is deferred. -+--------+-------------+-------------+--------------+ ++---------------------------------------------------+ |one_time|Use: redirect|Type: boolean|Default: false| -+--------+-------------+-------------+--------------+ ++---------------------------------------------------+ Sometimes the fact that Exim re-evaluates aliases and reprocesses redirection files each time it tries to deliver a message causes a problem when one or more @@ -18301,25 +18637,25 @@ all_parents log selector is set. It is expected that one_time will typically be used for mailing lists, where there is normally just one level of expansion. -+------+-------------+-----------------+--------------+ ++-----------------------------------------------------+ |owners|Use: redirect|Type: string list|Default: unset| -+------+-------------+-----------------+--------------+ ++-----------------------------------------------------+ This specifies a list of permitted owners for the file specified by file. This list is in addition to the local user when check_local_user is set. See check_owner above. -+---------+-------------+-----------------+--------------+ ++--------------------------------------------------------+ |owngroups|Use: redirect|Type: string list|Default: unset| -+---------+-------------+-----------------+--------------+ ++--------------------------------------------------------+ This specifies a list of permitted groups for the file specified by file. The list is in addition to the local user's primary group when check_local_user is set. See check_group above. -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ |pipe_transport|Use: redirect|Type: string*|Default: unset| -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ A redirect router sets up a direct delivery to a pipe when a string starting with a vertical bar character is specified as a new "address". The transport @@ -18327,9 +18663,9 @@ configured transport. This should normally be a pipe transport. When the transport is run, the pipe command is in $address_pipe. -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ |qualify_domain|Use: redirect|Type: string*|Default: unset| -+--------------+-------------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set, and an unqualified address (one without a domain) is generated, and that address would normally be qualified by the global setting @@ -18342,9 +18678,9 @@ for traditional .forward files, it applies only to addresses that are not preceded by a backslash. Sieve filters cannot generate unqualified addresses. -+-----------------------+-------------+-------------+--------------+ ++------------------------------------------------------------------+ |qualify_preserve_domain|Use: redirect|Type: boolean|Default: false| -+-----------------------+-------------+-------------+--------------+ ++------------------------------------------------------------------+ If this option is set, the router's local qualify_domain option must not be set (a configuration error occurs if it is). If an unqualified address (one without @@ -18353,9 +18689,9 @@ value. In the case of a traditional .forward file, this applies whether or not the address is preceded by a backslash. -+----------+-------------+-------------+-------------+ ++----------------------------------------------------+ |repeat_use|Use: redirect|Type: boolean|Default: true| -+----------+-------------+-------------+-------------+ ++----------------------------------------------------+ If this option is set false, the router is skipped for a child address that has any ancestor that was routed by this router. This test happens before any of @@ -18363,9 +18699,9 @@ when the ancestor is the same as the current address. See also check_ancestor above and the generic redirect_router option. -+---------------+-------------+-------------+--------------+ ++----------------------------------------------------------+ |reply_transport|Use: redirect|Type: string*|Default: unset| -+---------------+-------------+-------------+--------------+ ++----------------------------------------------------------+ A redirect router sets up an automatic reply when a mail or vacation command is used in a filter file. The transport used is specified by this option, which, @@ -18373,32 +18709,32 @@ normally be an autoreply transport. Other transports are unlikely to do anything sensible or useful. -+-------+-------------+-------------+-------------+ ++-------------------------------------------------+ |rewrite|Use: redirect|Type: boolean|Default: true| -+-------+-------------+-------------+-------------+ ++-------------------------------------------------+ If this option is set false, addresses generated by the router are not subject to address rewriting. Otherwise, they are treated like new addresses and are rewritten according to the global rewriting rules. -+----------------+-------------+-------------+--------------+ ++-----------------------------------------------------------+ |sieve_subaddress|Use: redirect|Type: string*|Default: unset| -+----------------+-------------+-------------+--------------+ ++-----------------------------------------------------------+ The value of this option is passed to a Sieve filter to specify the :subaddress part of an address. -+-----------------+-------------+-------------+--------------+ ++------------------------------------------------------------+ |sieve_useraddress|Use: redirect|Type: string*|Default: unset| -+-----------------+-------------+-------------+--------------+ ++------------------------------------------------------------+ The value of this option is passed to a Sieve filter to specify the :user part of an address. However, if it is unset, the entire original local part (including any prefix or suffix) is used for :user. -+------------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------------+ |sieve_vacation_directory|Use: redirect|Type: string*|Default: unset| -+------------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------------+ To enable the "vacation" extension for Sieve filters, you must set sieve_vacation_directory to the directory where vacation databases are held (do @@ -18406,9 +18742,9 @@ option refers to an autoreply transport. Each user needs their own directory; Exim will create it if necessary. -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ |skip_syntax_errors|Use: redirect|Type: boolean|Default: false| -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ If skip_syntax_errors is set, syntactically malformed addresses in non-filter redirection data are skipped, and each failing address is logged. If @@ -18476,15 +18812,15 @@ condition = ${if match {$sender_host_address}\ {\N^(|127\.0\.0\.1)$\N}} -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ |syntax_errors_text|Use: redirect|Type: string*|Default: unset| -+------------------+-------------+-------------+--------------+ ++-------------------------------------------------------------+ See skip_syntax_errors above. -+----------------+-------------+------------+--------------+ ++----------------------------------------------------------+ |syntax_errors_to|Use: redirect|Type: string|Default: unset| -+----------------+-------------+------------+--------------+ ++----------------------------------------------------------+ See skip_syntax_errors above. @@ -18527,7 +18863,7 @@ This is supposed to write the message at the end of the file. However, if two messages arrive at the same time, the file will be scrambled. You can use the -exim_lock utility program (see section 52.15) to lock a file using the same +exim_lock utility program (see section 53.15) to lock a file using the same algorithm that Exim itself uses. @@ -18651,35 +18987,35 @@ The following generic options apply to all transports: -+---------+---------------+-------------+--------------+ ++------------------------------------------------------+ |body_only|Use: transports|Type: boolean|Default: false| -+---------+---------------+-------------+--------------+ ++------------------------------------------------------+ If this option is set, the message's headers are not transported. It is mutually exclusive with headers_only. If it is used with the appendfile or pipe transports, the settings of message_prefix and message_suffix should be checked, because this option does not automatically suppress them. -+-----------------+---------------+-------------+--------------+ ++--------------------------------------------------------------+ |current_directory|Use: transports|Type: string*|Default: unset| -+-----------------+---------------+-------------+--------------+ ++--------------------------------------------------------------+ This specifies the current directory that is to be set while running the transport, overriding any value that may have been set by the router. If the expansion fails for any reason, including forced failure, an error is logged, and delivery is deferred. -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ |disable_logging|Use: transports|Type: boolean|Default: false| -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ If this option is set true, nothing is logged for any deliveries by the transport or for any transport errors. You should not set this option unless you really, really know what you are doing. -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ |debug_print|Use: transports|Type: string*|Default: unset| -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ If this option is set and debugging is enabled (see the -d command line option), the string is expanded and included in the debugging output when the @@ -18692,9 +19028,9 @@ variables $transport_name and $router_name contain the name of the transport and the router that called it. -+-----------------+---------------+-------------+--------------+ ++--------------------------------------------------------------+ |delivery_date_add|Use: transports|Type: boolean|Default: false| -+-----------------+---------------+-------------+--------------+ ++--------------------------------------------------------------+ If this option is true, a Delivery-date: header is added to the message. This gives the actual time the delivery was made. As this is not a standard header, @@ -18702,16 +19038,16 @@ removal from incoming messages, so that delivered messages can safely be resent to other recipients. -+------+---------------+------------+--------------+ ++--------------------------------------------------+ |driver|Use: transports|Type: string|Default: unset| -+------+---------------+------------+--------------+ ++--------------------------------------------------+ This specifies which of the available transport drivers is to be used. There is no default, and this option must be set for every transport. -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ |envelope_to_add|Use: transports|Type: boolean|Default: false| -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ If this option is true, an Envelope-to: header is added to the message. This gives the original address(es) in the incoming envelope that caused this @@ -18722,54 +19058,64 @@ removal from incoming messages, so that delivered messages can safely be resent to other recipients. -+-----+---------------+-------------+-------------------+ ++-------------------------------------------------------+ |group|Use: transports|Type: string*|Default: Exim group| -+-----+---------------+-------------+-------------------+ ++-------------------------------------------------------+ This option specifies a gid for running the transport process, overriding any value that the router supplies, and also overriding any value associated with user (see below). -+-----------+---------------+-----------+--------------+ ++------------------------------------------------------+ |headers_add|Use: transports|Type: list*|Default: unset| -+-----------+---------------+-----------+--------------+ ++------------------------------------------------------+ -This option specifies a list of text headers, newline-separated, which are -(separately) expanded and added to the header portion of a message as it is -transported, as described in section 46.17. Additional header lines can also be -specified by routers. If the result of the expansion is an empty string, or if -the expansion is forced to fail, no action is taken. Other expansion failures -are treated as errors and cause the delivery to be deferred. +This option specifies a list of text headers, + +newline-separated (by default, changeable in the usual way), + +which are (separately) expanded and added to the header portion of a message as +it is transported, as described in section 47.17. Additional header lines can +also be specified by routers. If the result of the expansion is an empty +string, or if the expansion is forced to fail, no action is taken. Other +expansion failures are treated as errors and cause the delivery to be deferred. Unlike most options, headers_add can be specified multiple times for a transport; all listed headers are added. -+------------+---------------+-------------+--------------+ ++---------------------------------------------------------+ |headers_only|Use: transports|Type: boolean|Default: false| -+------------+---------------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set, the message's body is not transported. It is mutually exclusive with body_only. If it is used with the appendfile or pipe transports, the settings of message_prefix and message_suffix should be checked, since this option does not automatically suppress them. -+--------------+---------------+-----------+--------------+ ++---------------------------------------------------------+ |headers_remove|Use: transports|Type: list*|Default: unset| -+--------------+---------------+-----------+--------------+ ++---------------------------------------------------------+ + +This option specifies a list of header names, + +colon-separated (by default, changeable in the usual way); -This option specifies a list of header names, colon-separated; these headers -are omitted from the message as it is transported, as described in section -46.17. Header removal can also be specified by routers. Each list item is -separately expanded. If the result of the expansion is an empty string, or if -the expansion is forced to fail, no action is taken. Other expansion failures -are treated as errors and cause the delivery to be deferred. +these headers are omitted from the message as it is transported, as described +in section 47.17. Header removal can also be specified by routers. Each list +item is separately expanded. If the result of the expansion is an empty string, +or if the expansion is forced to fail, no action is taken. Other expansion +failures are treated as errors and cause the delivery to be deferred. Unlike most options, headers_remove can be specified multiple times for a router; all listed headers are removed. -+---------------+---------------+------------+--------------+ +Warning: Because of the separate expansion of the list items, items that +contain a list separator must have it doubled. To avoid this, change the list +separator (6.20). + ++-----------------------------------------------------------+ |headers_rewrite|Use: transports|Type: string|Default: unset| -+---------------+---------------+------------+--------------+ ++-----------------------------------------------------------+ This option allows addresses in header lines to be rewritten at transport time, that is, as the message is being copied to its destination. The contents of the @@ -18790,9 +19136,9 @@ change the return path using return_path, but you cannot change envelope recipients at this time. -+--------------+---------------+-------------+--------------+ ++-----------------------------------------------------------+ |home_directory|Use: transports|Type: string*|Default: unset| -+--------------+---------------+-------------+--------------+ ++-----------------------------------------------------------+ This option specifies a home directory setting for a local transport, overriding any value that may be set by the router. The home directory is @@ -18802,17 +19148,17 @@ option on the router. If the expansion fails for any reason, including forced failure, an error is logged, and delivery is deferred. -+----------+---------------+-------------+--------------+ ++-------------------------------------------------------+ |initgroups|Use: transports|Type: boolean|Default: false| -+----------+---------------+-------------+--------------+ ++-------------------------------------------------------+ If this option is true and the uid for the delivery process is provided by the transport, the initgroups() function is called when running the transport to ensure that any additional groups associated with the uid are set up. -+------------------+---------------+-------------+----------+ ++-----------------------------------------------------------+ |message_size_limit|Use: transports|Type: string*|Default: 0| -+------------------+---------------+-------------+----------+ ++-----------------------------------------------------------+ This option controls the size of messages passed through the transport. It is expanded before use; the result of the expansion must be a sequence of decimal @@ -18824,9 +19170,9 @@ ensure that return_size_limit is less than the transport's message_size_limit, as otherwise the bounce message will fail to get delivered. -+--------------------+---------------+-------------+--------------+ ++-----------------------------------------------------------------+ |rcpt_include_affixes|Use: transports|Type: boolean|Default: false| -+--------------------+---------------+-------------+--------------+ ++-----------------------------------------------------------------+ When this option is false (the default), and an address that has had any affixes (prefixes or suffixes) removed from the local part is delivered by any @@ -18846,9 +19192,9 @@ deliveries by the appendfile and pipe transports as well as to the lmtp and smtp transports. -+--------------------+---------------+-------------+------------------+ ++---------------------------------------------------------------------+ |retry_use_local_part|Use: transports|Type: boolean|Default: see below| -+--------------------+---------------+-------------+------------------+ ++---------------------------------------------------------------------+ When a delivery suffers a temporary failure, a retry record is created in Exim's hints database. For remote deliveries, the key for the retry record is @@ -18867,9 +19213,9 @@ the default value is false for tidiness, but changing the value has no effect on a remote transport in the current implementation. -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ |return_path|Use: transports|Type: string*|Default: unset| -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ If this option is set, the string is expanded at transport time and replaces the existing return path (envelope sender) value in the copy of the message @@ -18886,7 +19232,7 @@ the message's envelope sender, or an address set by the errors_to option on a router. If the expansion is forced to fail, no replacement occurs; if it fails for another reason, delivery is deferred. This option can be used to support -VERP (Variable Envelope Return Paths) - see section 49.6. +VERP (Variable Envelope Return Paths) - see section 50.6. Note: If a delivery error is detected locally, including the case when a remote server rejects a message at SMTP time, the bounce message is not sent to the @@ -18894,9 +19240,9 @@ defaults to the incoming sender address, but can be changed by setting errors_to in a router. -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ |return_path_add|Use: transports|Type: boolean|Default: false| -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ If this option is true, a Return-path: header is added to the message. Although the return path is normally available in the prefix line of BSD mailboxes, this @@ -18909,15 +19255,15 @@ return_path_remove, which requests removal of this header from incoming messages, so that delivered messages can safely be resent to other recipients. -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ |shadow_condition|Use: transports|Type: string*|Default: unset| -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ See shadow_transport below. -+----------------+---------------+------------+--------------+ ++------------------------------------------------------------+ |shadow_transport|Use: transports|Type: string|Default: unset| -+----------------+---------------+------------+--------------+ ++------------------------------------------------------------+ A local transport may set the shadow_transport option to the name of another local transport. Shadow remote transports are not supported. @@ -18944,9 +19290,9 @@ provides, and implementing automatic acknowledgment policies based on message headers that some sites insist on. -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ |transport_filter|Use: transports|Type: string*|Default: unset| -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ This option sets up a filtering (in the Unix shell sense) process for messages at transport time. It should not be confused with mail filtering as set up by @@ -19049,9 +19395,9 @@ passed through the filter as it is being copied into the newly generated message, which happens if the return_message option is set. -+------------------------+---------------+----------+-----------+ ++---------------------------------------------------------------+ |transport_filter_timeout|Use: transports|Type: time|Default: 5m| -+------------------------+---------------+----------+-----------+ ++---------------------------------------------------------------+ When Exim is reading the output of a transport filter, it applies a timeout that can be set by this option. Exceeding the timeout is normally treated as a @@ -19061,9 +19407,9 @@ if the pipe transport's timeout_defer option is set true, it becomes a temporary error. -+----+---------------+-------------+------------------+ ++-----------------------------------------------------+ |user|Use: transports|Type: string*|Default: Exim user| -+----+---------------+-------------+------------------+ ++-----------------------------------------------------+ This option specifies the user under whose uid the delivery process is to be run, overriding any uid that may have been set by the router. If the user is @@ -19146,7 +19492,7 @@ escape_string = ".." when batched SMTP is in use. A full description of the batch SMTP mechanism is -given in section 47.10. The lmtp transport does not have a use_bsmtp option, +given in section 48.10. The lmtp transport does not have a use_bsmtp option, because it always delivers using the SMTP protocol. If the generic envelope_to_add option is set for a batching transport, the @@ -19272,17 +19618,17 @@ 26.2 Private options for appendfile ----------------------------------- -+----------+---------------+-------------+--------------+ ++-------------------------------------------------------+ |allow_fifo|Use: appendfile|Type: boolean|Default: false| -+----------+---------------+-------------+--------------+ ++-------------------------------------------------------+ Setting this option permits delivery to named pipes (FIFOs) as well as to regular files. If no process is reading the named pipe at delivery time, the delivery is deferred. -+-------------+---------------+-------------+--------------+ ++----------------------------------------------------------+ |allow_symlink|Use: appendfile|Type: boolean|Default: false| -+-------------+---------------+-------------+--------------+ ++----------------------------------------------------------+ By default, appendfile will not deliver if the path name for the file is that of a symbolic link. Setting this option relaxes that constraint, but there are @@ -19290,40 +19636,40 @@ you are doing if you set this. Details of exactly what this option affects are included in the discussion which follows this list of options. -+--------+---------------+-------------+--------------+ ++-----------------------------------------------------+ |batch_id|Use: appendfile|Type: string*|Default: unset| -+--------+---------------+-------------+--------------+ ++-----------------------------------------------------+ See the description of local delivery batching in chapter 25. However, batching is automatically disabled for appendfile deliveries that happen as a result of forwarding or aliasing or other redirection directly to a file. -+---------+---------------+-------------+----------+ ++--------------------------------------------------+ |batch_max|Use: appendfile|Type: integer|Default: 1| -+---------+---------------+-------------+----------+ ++--------------------------------------------------+ See the description of local delivery batching in chapter 25. -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ |check_group|Use: appendfile|Type: boolean|Default: false| -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ When this option is set, the group owner of the file defined by the file option is checked to see that it is the same as the group under which the delivery process is running. The default setting is false because the default file mode is 0600, which means that the group is irrelevant. -+-----------+---------------+-------------+-------------+ ++-------------------------------------------------------+ |check_owner|Use: appendfile|Type: boolean|Default: true| -+-----------+---------------+-------------+-------------+ ++-------------------------------------------------------+ When this option is set, the owner of the file defined by the file option is checked to ensure that it is the same as the user under which the delivery process is running. -+------------+---------------+------------+------------------+ ++------------------------------------------------------------+ |check_string|Use: appendfile|Type: string|Default: see below| -+------------+---------------+------------+------------------+ ++------------------------------------------------------------+ As appendfile writes the message, the start of each line is tested for matching check_string, and if it does, the initial matching characters are replaced by @@ -19346,9 +19692,9 @@ message_prefix = "\1\1\1\1\n" message_suffix = "\1\1\1\1\n" -+----------------+---------------+-------------+-------------+ ++------------------------------------------------------------+ |create_directory|Use: appendfile|Type: boolean|Default: true| -+----------------+---------------+-------------+-------------+ ++------------------------------------------------------------+ When this option is true, Exim attempts to create any missing superior directories for the file that it is about to write. A created directory's mode @@ -19360,9 +19706,9 @@ is propagated to the child; if not, the currently set group is used. However, in FreeBSD, the parent's group is always used. -+-----------+---------------+------------+-----------------+ ++----------------------------------------------------------+ |create_file|Use: appendfile|Type: string|Default: anywhere| -+-----------+---------------+------------+-----------------+ ++----------------------------------------------------------+ This option constrains the location of files and directories that are created by this transport. It applies to files defined by the file option and @@ -19376,9 +19722,9 @@ names are generated from users' .forward files. These are usually handled by an appendfile transport called address_file. See also file_must_exist. -+---------+---------------+-------------+--------------+ ++------------------------------------------------------+ |directory|Use: appendfile|Type: string*|Default: unset| -+---------+---------------+-------------+--------------+ ++------------------------------------------------------+ This option is mutually exclusive with the file option, but one of file or directory must be set, unless the delivery is the direct result of a @@ -19390,9 +19736,9 @@ (see maildir_format and mailstore_format), and see section 26.4 for further details of this form of delivery. -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ |directory_file|Use: appendfile|Type: string*|Default: see below| -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ When directory is set, but neither maildir_format nor mailstore_format is set, appendfile delivers each message into a file whose name is obtained by @@ -19404,22 +19750,22 @@ inode of the file. The variable $inode is available only when expanding this option. -+--------------+---------------+-------------------+-------------+ ++----------------------------------------------------------------+ |directory_mode|Use: appendfile|Type: octal integer|Default: 0700| -+--------------+---------------+-------------------+-------------+ ++----------------------------------------------------------------+ If appendfile creates any directories as a result of the create_directory option, their mode is specified by this option. -+-------------+---------------+------------+------------------------+ ++-------------------------------------------------------------------+ |escape_string|Use: appendfile|Type: string|Default: see description| -+-------------+---------------+------------+------------------------+ ++-------------------------------------------------------------------+ See check_string above. -+----+---------------+-------------+--------------+ ++-------------------------------------------------+ |file|Use: appendfile|Type: string*|Default: unset| -+----+---------------+-------------+--------------+ ++-------------------------------------------------+ This option is mutually exclusive with the directory option, but one of file or directory must be set, unless the delivery is the direct result of a @@ -19444,9 +19790,9 @@ deliveries to be possible, or alternatively the group option can be used to run the delivery under a group id which has write access to the directory. -+-----------+---------------+------------+--------------+ ++-------------------------------------------------------+ |file_format|Use: appendfile|Type: string|Default: unset| -+-----------+---------------+------------+--------------+ ++-------------------------------------------------------+ This option requests the transport to check the format of an existing file before adding to it. The check consists of matching a specific string at the @@ -19468,17 +19814,17 @@ any string, or if the transport named for a given string is not defined, delivery is deferred. -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ |file_must_exist|Use: appendfile|Type: boolean|Default: false| -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ If this option is true, the file specified by the file option must exist. A temporary error occurs if it does not, causing delivery to be deferred. If this option is false, the file is created if it does not exist. -+------------------+---------------+----------+-----------+ ++---------------------------------------------------------+ |lock_fcntl_timeout|Use: appendfile|Type: time|Default: 0s| -+------------------+---------------+----------+-----------+ ++---------------------------------------------------------+ By default, the appendfile transport uses non-blocking calls to fcntl() when locking an open mailbox file. If the call fails, the delivery process sleeps @@ -19509,54 +19855,54 @@ failed to lock mailbox /some/file (fcntl) -+------------------+---------------+----------+-----------+ ++---------------------------------------------------------+ |lock_flock_timeout|Use: appendfile|Type: time|Default: 0s| -+------------------+---------------+----------+-----------+ ++---------------------------------------------------------+ This timeout applies to file locking when using flock() (see use_flock); the timeout operates in a similar manner to lock_fcntl_timeout. -+-------------+---------------+----------+-----------+ ++----------------------------------------------------+ |lock_interval|Use: appendfile|Type: time|Default: 3s| -+-------------+---------------+----------+-----------+ ++----------------------------------------------------+ This specifies the time to wait between attempts to lock the file. See below for details of locking. -+------------+---------------+-------------+-----------+ ++------------------------------------------------------+ |lock_retries|Use: appendfile|Type: integer|Default: 10| -+------------+---------------+-------------+-----------+ ++------------------------------------------------------+ This specifies the maximum number of attempts to lock the file. A value of zero is treated as 1. See below for details of locking. -+-------------+---------------+-------------------+-------------+ ++---------------------------------------------------------------+ |lockfile_mode|Use: appendfile|Type: octal integer|Default: 0600| -+-------------+---------------+-------------------+-------------+ ++---------------------------------------------------------------+ This specifies the mode of the created lock file, when a lock file is being used (see use_lockfile and use_mbx_lock). -+----------------+---------------+----------+------------+ ++--------------------------------------------------------+ |lockfile_timeout|Use: appendfile|Type: time|Default: 30m| -+----------------+---------------+----------+------------+ ++--------------------------------------------------------+ When a lock file is being used (see use_lockfile), if a lock file already exists and is older than this value, it is assumed to have been left behind by accident, and Exim attempts to remove it. -+-----------------+---------------+-------------+--------------+ ++--------------------------------------------------------------+ |mailbox_filecount|Use: appendfile|Type: string*|Default: unset| -+-----------------+---------------+-------------+--------------+ ++--------------------------------------------------------------+ If this option is set, it is expanded, and the result is taken as the current number of files in the mailbox. It must be a decimal number, optionally followed by K or M. This provides a way of obtaining this information from an external source that maintains the data. -+------------+---------------+-------------+--------------+ ++---------------------------------------------------------+ |mailbox_size|Use: appendfile|Type: string*|Default: unset| -+------------+---------------+-------------+--------------+ ++---------------------------------------------------------+ If this option is set, it is expanded, and the result is taken as the current size the mailbox. It must be a decimal number, optionally followed by K or M. @@ -19564,9 +19910,9 @@ maintains the data. This is likely to be helpful for maildir deliveries where it is computationally expensive to compute the size of a mailbox. -+--------------+---------------+-------------+--------------+ ++-----------------------------------------------------------+ |maildir_format|Use: appendfile|Type: boolean|Default: false| -+--------------+---------------+-------------+--------------+ ++-----------------------------------------------------------+ If this option is set with the directory option, the delivery is into a new file, in the "maildir" format that is used by other mail software. When the @@ -19576,9 +19922,9 @@ or not it ends with "/". This option is available only if SUPPORT_MAILDIR is present in Local/Makefile. See section 26.5 below for further details. -+-----------------------------+---------------+------------+------------------+ ++-----------------------------------------------------------------------------+ |maildir_quota_directory_regex|Use: appendfile|Type: string|Default: See below| -+-----------------------------+---------------+------------+------------------+ ++-----------------------------------------------------------------------------+ This option is relevant only when maildir_use_size_file is set. It defines a regular expression for specifying directories, relative to the quota directory @@ -19598,23 +19944,23 @@ calculations, quota processing is bypassed for any messages that are delivered directly into that directory. -+---------------+---------------+-------------+-----------+ ++---------------------------------------------------------+ |maildir_retries|Use: appendfile|Type: integer|Default: 10| -+---------------+---------------+-------------+-----------+ ++---------------------------------------------------------+ This option specifies the number of times to retry when writing a file in "maildir" format. See section 26.5 below. -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ |maildir_tag|Use: appendfile|Type: string*|Default: unset| -+-----------+---------------+-------------+--------------+ ++--------------------------------------------------------+ This option applies only to deliveries in maildir format, and is described in section 26.5 below. -+---------------------+----------------+-------------+--------------+ ++-------------------------------------------------------------------+ |maildir_use_size_file|Use: appendfile*|Type: boolean|Default: false| -+---------------------+----------------+-------------+--------------+ ++-------------------------------------------------------------------+ The result of string expansion for this option must be a valid boolean value. If it is true, it enables support for maildirsize files. Exim creates a @@ -19622,9 +19968,9 @@ quota option of the transport. If quota is unset, the value is zero. See maildir_quota_directory_regex above and section 26.5 below for further details. -+--------------------------+---------------+------------+--------------+ ++----------------------------------------------------------------------+ |maildirfolder_create_regex|Use: appendfile|Type: string|Default: unset| -+--------------------------+---------------+------------+--------------+ ++----------------------------------------------------------------------+ The value of this option is a regular expression. If it is unset, it has no effect. Otherwise, before a maildir delivery takes place, the pattern is @@ -19634,31 +19980,31 @@ maildirfolder in the directory, and creates it if it does not exist. See section 26.5 for more details. -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ |mailstore_format|Use: appendfile|Type: boolean|Default: false| -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ If this option is set with the directory option, the delivery is into two new files in "mailstore" format. The option is available only if SUPPORT_MAILSTORE is present in Local/Makefile. See section 26.4 below for further details. -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ |mailstore_prefix|Use: appendfile|Type: string*|Default: unset| -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ This option applies only to deliveries in mailstore format, and is described in section 26.4 below. -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ |mailstore_suffix|Use: appendfile|Type: string*|Default: unset| -+----------------+---------------+-------------+--------------+ ++-------------------------------------------------------------+ This option applies only to deliveries in mailstore format, and is described in section 26.4 below. -+----------+---------------+-------------+--------------+ ++-------------------------------------------------------+ |mbx_format|Use: appendfile|Type: boolean|Default: false| -+----------+---------------+-------------+--------------+ ++-------------------------------------------------------+ This option is available only if Exim has been compiled with SUPPORT_MBX set in Local/Makefile. If mbx_format is set with the file option, the message is @@ -19688,9 +20034,9 @@ means for the whole of a Pine or IMAP session), Exim will not be able to append messages to it. -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ |message_prefix|Use: appendfile|Type: string*|Default: see below| -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ The string specified here is expanded and output at the start of every message. The default is unset unless file is specified and use_bsmtp is not set, in @@ -19702,9 +20048,9 @@ Note: If you set use_crlf true, you must change any occurrences of "\n" to "\r\ n" in message_prefix. -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ |message_suffix|Use: appendfile|Type: string*|Default: see below| -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ The string specified here is expanded and output at the end of every message. The default is unset unless file is specified and use_bsmtp is not set, in @@ -19716,9 +20062,9 @@ Note: If you set use_crlf true, you must change any occurrences of "\n" to "\r\ n" in message_suffix. -+----+---------------+-------------------+-------------+ ++------------------------------------------------------+ |mode|Use: appendfile|Type: octal integer|Default: 0600| -+----+---------------+-------------------+-------------+ ++------------------------------------------------------+ If the output file is created, it is given this mode. If it already exists and has wider permissions, they are reduced to this mode. If it has narrower @@ -19727,26 +20073,26 @@ particular mode, the mode of the output file is always forced to take that value, and this option is ignored. -+------------------+---------------+-------------+-------------+ ++--------------------------------------------------------------+ |mode_fail_narrower|Use: appendfile|Type: boolean|Default: true| -+------------------+---------------+-------------+-------------+ ++--------------------------------------------------------------+ This option applies in the case when an existing mailbox file has a narrower mode than that specified by the mode option. If mode_fail_narrower is true, the delivery is deferred ("mailbox has the wrong mode"); otherwise Exim continues with the delivery attempt, using the existing mode of the file. -+-------------+---------------+-------------+--------------+ ++----------------------------------------------------------+ |notify_comsat|Use: appendfile|Type: boolean|Default: false| -+-------------+---------------+-------------+--------------+ ++----------------------------------------------------------+ If this option is true, the comsat daemon is notified after every successful delivery to a user mailbox. This is the daemon that notifies logged on users about incoming mail. -+-----+---------------+-------------+--------------+ ++--------------------------------------------------+ |quota|Use: appendfile|Type: string*|Default: unset| -+-----+---------------+-------------+--------------+ ++--------------------------------------------------+ This option imposes a limit on the size of the file to which Exim is appending, or to the total space used in the directory tree when the directory option is @@ -19792,18 +20138,18 @@ continue until the quota has been exceeded; thereafter, no further messages are delivered. See also quota_warn_threshold. -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ |quota_directory|Use: appendfile|Type: string*|Default: unset| -+---------------+---------------+-------------+--------------+ ++------------------------------------------------------------+ This option defines the directory to check for quota purposes when delivering into individual files. The default is the delivery directory, or, if a file called maildirfolder exists in a maildir directory, the parent of the delivery directory. -+---------------+---------------+-------------+----------+ ++--------------------------------------------------------+ |quota_filecount|Use: appendfile|Type: string*|Default: 0| -+---------------+---------------+-------------+----------+ ++--------------------------------------------------------+ This option applies when the directory option is set. It limits the total number of files in the directory (compare the inode limit in system quotas). It @@ -19811,15 +20157,15 @@ failure causes delivery to be deferred. A value of zero is interpreted as "no quota". -+------------------+---------------+-------------+-------------+ ++--------------------------------------------------------------+ |quota_is_inclusive|Use: appendfile|Type: boolean|Default: true| -+------------------+---------------+-------------+-------------+ ++--------------------------------------------------------------+ See quota above. -+----------------+---------------+------------+--------------+ ++------------------------------------------------------------+ |quota_size_regex|Use: appendfile|Type: string|Default: unset| -+----------------+---------------+------------+--------------+ ++------------------------------------------------------------+ This option applies when one of the delivery modes that writes a separate file for each message is being used. When Exim wants to find the size of one of @@ -19845,9 +20191,9 @@ Section 26.7 contains further information. -+------------------+---------------+-------------+------------------+ ++-------------------------------------------------------------------+ |quota_warn_message|Use: appendfile|Type: string*|Default: see below| -+------------------+---------------+-------------+------------------+ ++-------------------------------------------------------------------+ See below for the use of this option. If it is not set when quota_warn_threshold is set, it defaults to @@ -19861,9 +20207,9 @@ a warning threshold that is\n\ set by the system administrator.\n" -+--------------------+---------------+-------------+----------+ ++-------------------------------------------------------------+ |quota_warn_threshold|Use: appendfile|Type: string*|Default: 0| -+--------------------+---------------+-------------+----------+ ++-------------------------------------------------------------+ This option is expanded in the same way as quota (see above). If the resulting value is greater than zero, and delivery of the message causes the size of the @@ -19892,18 +20238,18 @@ independent of one another except when the threshold is specified as a percentage. -+---------+---------------+-------------+--------------+ ++------------------------------------------------------+ |use_bsmtp|Use: appendfile|Type: boolean|Default: false| -+---------+---------------+-------------+--------------+ ++------------------------------------------------------+ If this option is set true, appendfile writes messages in "batch SMTP" format, with the envelope sender and recipient(s) included as SMTP commands. If you want to include a leading HELO command with such messages, you can do so by -setting the message_prefix option. See section 47.10 for details of batch SMTP. +setting the message_prefix option. See section 48.10 for details of batch SMTP. -+--------+---------------+-------------+--------------+ ++-----------------------------------------------------+ |use_crlf|Use: appendfile|Type: boolean|Default: false| -+--------+---------------+-------------+--------------+ ++-----------------------------------------------------+ This option causes lines to be terminated with the two-character CRLF sequence (carriage return, linefeed) instead of just a linefeed character. In the case @@ -19917,9 +20263,9 @@ have non-empty defaults, the values end with a single linefeed, so they must be changed to end with "\r\n" if use_crlf is set. -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ |use_fcntl_lock|Use: appendfile|Type: boolean|Default: see below| -+--------------+---------------+-------------+------------------+ ++---------------------------------------------------------------+ This option controls the use of the fcntl() function to lock a file for exclusive use when a message is being appended. It is set by default unless @@ -19927,9 +20273,9 @@ all your MUAs use lock file locking. When both use_fcntl_lock and use_flock_lock are unset, use_lockfile must be set. -+--------------+---------------+-------------+--------------+ ++-----------------------------------------------------------+ |use_flock_lock|Use: appendfile|Type: boolean|Default: false| -+--------------+---------------+-------------+--------------+ ++-----------------------------------------------------------+ This option is provided to support the use of flock() for file locking, for the few situations where it is needed. Most modern operating systems support fcntl @@ -19949,9 +20295,9 @@ Warning: flock() locks do not work on NFS files (unless flock() is just being mapped onto fcntl() by the OS). -+------------+---------------+-------------+------------------+ ++-------------------------------------------------------------+ |use_lockfile|Use: appendfile|Type: boolean|Default: see below| -+------------+---------------+-------------+------------------+ ++-------------------------------------------------------------+ If this option is turned off, Exim does not attempt to create a lock file when appending to a mailbox file. In this situation, the only locking is by fcntl(). @@ -19969,9 +20315,9 @@ possible to turn both use_lockfile and use_fcntl_lock off, except when mbx_format is set. -+------------+---------------+-------------+------------------+ ++-------------------------------------------------------------+ |use_mbx_lock|Use: appendfile|Type: boolean|Default: see below| -+------------+---------------+-------------+------------------+ ++-------------------------------------------------------------+ This option is available only if Exim has been compiled with SUPPORT_MBX set in Local/Makefile. Setting the option specifies that special MBX locking rules be @@ -20312,7 +20658,7 @@ If neither maildir_format nor mailstore_format is set, a single new file is created directly in the named directory. For example, when delivering messages into files in batched SMTP format for later delivery to some host (see section -47.10), a setting such as +48.10), a setting such as directory = /var/bsmtp/$host @@ -20388,82 +20734,82 @@ 27.1 Private options for autoreply ---------------------------------- -+---+--------------+-------------+--------------+ ++-----------------------------------------------+ |bcc|Use: autoreply|Type: string*|Default: unset| -+---+--------------+-------------+--------------+ ++-----------------------------------------------+ This specifies the addresses that are to receive "blind carbon copies" of the message when the message is specified by the transport. -+--+--------------+-------------+--------------+ ++----------------------------------------------+ |cc|Use: autoreply|Type: string*|Default: unset| -+--+--------------+-------------+--------------+ ++----------------------------------------------+ This specifies recipients of the message and the contents of the Cc: header when the message is specified by the transport. -+----+--------------+-------------+--------------+ ++------------------------------------------------+ |file|Use: autoreply|Type: string*|Default: unset| -+----+--------------+-------------+--------------+ ++------------------------------------------------+ The contents of the file are sent as the body of the message when the message is specified by the transport. If both file and text are set, the text string comes first. -+-----------+--------------+-------------+--------------+ ++-------------------------------------------------------+ |file_expand|Use: autoreply|Type: boolean|Default: false| -+-----------+--------------+-------------+--------------+ ++-------------------------------------------------------+ If this is set, the contents of the file named by the file option are subjected to string expansion as they are added to the message. -+-------------+--------------+-------------+--------------+ ++---------------------------------------------------------+ |file_optional|Use: autoreply|Type: boolean|Default: false| -+-------------+--------------+-------------+--------------+ ++---------------------------------------------------------+ If this option is true, no error is generated if the file named by the file option or passed with the address does not exist or cannot be read. -+----+--------------+-------------+--------------+ ++------------------------------------------------+ |from|Use: autoreply|Type: string*|Default: unset| -+----+--------------+-------------+--------------+ ++------------------------------------------------+ This specifies the contents of the From: header when the message is specified by the transport. -+-------+--------------+-------------+--------------+ ++---------------------------------------------------+ |headers|Use: autoreply|Type: string*|Default: unset| -+-------+--------------+-------------+--------------+ ++---------------------------------------------------+ This specifies additional RFC 2822 headers that are to be added to the message when the message is specified by the transport. Several can be given by using " \n" to separate them. There is no check on the format. -+---+--------------+-------------+--------------+ ++-----------------------------------------------+ |log|Use: autoreply|Type: string*|Default: unset| -+---+--------------+-------------+--------------+ ++-----------------------------------------------+ This option names a file in which a record of every message sent is logged when the message is specified by the transport. -+----+--------------+-------------------+-------------+ ++-----------------------------------------------------+ |mode|Use: autoreply|Type: octal integer|Default: 0600| -+----+--------------+-------------------+-------------+ ++-----------------------------------------------------+ If either the log file or the "once" file has to be created, this mode is used. -+----------+--------------+-------------------+--------------+ ++------------------------------------------------------------+ |never_mail|Use: autoreply|Type: address list*|Default: unset| -+----------+--------------+-------------------+--------------+ ++------------------------------------------------------------+ If any run of the transport creates a message with a recipient that matches any item in the list, that recipient is quietly discarded. If all recipients are discarded, no message is created. This applies both when the recipients are generated by a filter and when they are specified in the transport. -+----+--------------+-------------+--------------+ ++------------------------------------------------+ |once|Use: autoreply|Type: string*|Default: unset| -+----+--------------+-------------+--------------+ ++------------------------------------------------+ This option names a file or DBM database in which a record of each To: recipient is kept when the message is specified by the transport. Note: This @@ -20490,37 +20836,37 @@ intervals that depend on the rate of turnover of addresses in the file. If once_repeat is set, it specifies a maximum time between repeats. -+--------------+--------------+-------------+----------+ ++------------------------------------------------------+ |once_file_size|Use: autoreply|Type: integer|Default: 0| -+--------------+--------------+-------------+----------+ ++------------------------------------------------------+ See once above. -+-----------+--------------+-----------+-----------+ ++--------------------------------------------------+ |once_repeat|Use: autoreply|Type: time*|Default: 0s| -+-----------+--------------+-----------+-----------+ ++--------------------------------------------------+ See once above. After expansion, the value of this option must be a valid time value. -+--------+--------------+-------------+--------------+ ++----------------------------------------------------+ |reply_to|Use: autoreply|Type: string*|Default: unset| -+--------+--------------+-------------+--------------+ ++----------------------------------------------------+ This specifies the contents of the Reply-To: header when the message is specified by the transport. -+--------------+--------------+-------------+--------------+ ++----------------------------------------------------------+ |return_message|Use: autoreply|Type: boolean|Default: false| -+--------------+--------------+-------------+--------------+ ++----------------------------------------------------------+ If this is set, a copy of the original message is returned with the new message, subject to the maximum size set in the return_size_limit global configuration option. -+-------+--------------+-------------+--------------+ ++---------------------------------------------------+ |subject|Use: autoreply|Type: string*|Default: unset| -+-------+--------------+-------------+--------------+ ++---------------------------------------------------+ This specifies the contents of the Subject: header when the message is specified by the transport. It is tempting to quote the original subject in @@ -20534,17 +20880,17 @@ non-bounce message to confirm a subscription, so the danger is relatively small. -+----+--------------+-------------+--------------+ ++------------------------------------------------+ |text|Use: autoreply|Type: string*|Default: unset| -+----+--------------+-------------+--------------+ ++------------------------------------------------+ This specifies a single string to be used as the body of the message when the message is specified by the transport. If both text and file are set, the text comes first. -+--+--------------+-------------+--------------+ ++----------------------------------------------+ |to|Use: autoreply|Type: string*|Default: unset| -+--+--------------+-------------+--------------+ ++----------------------------------------------+ This specifies recipients of the message and the contents of the To: header when the message is specified by the transport. @@ -20567,24 +20913,24 @@ is present in your Local/Makefile in order to have the lmtp transport included in the Exim binary. The private options of the lmtp transport are as follows: -+--------+---------+-------------+--------------+ ++-----------------------------------------------+ |batch_id|Use: lmtp|Type: string*|Default: unset| -+--------+---------+-------------+--------------+ ++-----------------------------------------------+ See the description of local delivery batching in chapter 25. -+---------+---------+-------------+----------+ ++--------------------------------------------+ |batch_max|Use: lmtp|Type: integer|Default: 1| -+---------+---------+-------------+----------+ ++--------------------------------------------+ This limits the number of addresses that can be handled in a single delivery. Most LMTP servers can handle several addresses at once, so it is normally a good idea to increase this value. See the description of local delivery batching in chapter 25. -+-------+---------+-------------+--------------+ ++----------------------------------------------+ |command|Use: lmtp|Type: string*|Default: unset| -+-------+---------+-------------+--------------+ ++----------------------------------------------+ This option must be set if socket is not set. The string is a command which is run in a separate process. It is split up into a command name and list of @@ -20593,25 +20939,25 @@ is passed to the new process using the standard input and output to operate the LMTP protocol. -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ |ignore_quota|Use: lmtp|Type: boolean|Default: false| -+------------+---------+-------------+--------------+ ++---------------------------------------------------+ If this option is set true, the string "IGNOREQUOTA" is added to RCPT commands, provided that the LMTP server has advertised support for IGNOREQUOTA in its response to the LHLO command. -+------+---------+-------------+--------------+ ++---------------------------------------------+ |socket|Use: lmtp|Type: string*|Default: unset| -+------+---------+-------------+--------------+ ++---------------------------------------------+ This option must be set if command is not set. The result of expansion must be the name of a Unix domain socket. The transport connects to the socket and delivers the message to it using the LMTP protocol. -+-------+---------+----------+-----------+ ++----------------------------------------+ |timeout|Use: lmtp|Type: time|Default: 5m| -+-------+---------+----------+-----------+ ++----------------------------------------+ The transport is aborted if the created process or Unix domain socket does not respond to LMTP commands or message input within this timeout. Delivery is @@ -20829,9 +21175,9 @@ 29.5 Private options for pipe ----------------------------- -+--------------+---------+------------------+--------------+ ++----------------------------------------------------------+ |allow_commands|Use: pipe|Type: string list*|Default: unset| -+--------------+---------+------------------+--------------+ ++----------------------------------------------------------+ The string is expanded, and is then interpreted as a colon-separated list of permitted commands. If restrict_to_path is not set, the only commands permitted @@ -20848,22 +21194,22 @@ and restrict_to_path is not set, the only permitted command is /usr/bin/ vacation. The allow_commands option may not be set if use_shell is set. -+--------+---------+-------------+--------------+ ++-----------------------------------------------+ |batch_id|Use: pipe|Type: string*|Default: unset| -+--------+---------+-------------+--------------+ ++-----------------------------------------------+ See the description of local delivery batching in chapter 25. -+---------+---------+-------------+----------+ ++--------------------------------------------+ |batch_max|Use: pipe|Type: integer|Default: 1| -+---------+---------+-------------+----------+ ++--------------------------------------------+ This limits the number of addresses that can be handled in a single delivery. See the description of local delivery batching in chapter 25. -+------------+---------+------------+--------------+ ++--------------------------------------------------+ |check_string|Use: pipe|Type: string|Default: unset| -+------------+---------+------------+--------------+ ++--------------------------------------------------+ As pipe writes the message, the start of each line is tested for matching check_string, and if it does, the initial matching characters are replaced by @@ -20873,9 +21219,9 @@ and escape_string are forced to values that implement the SMTP escaping protocol. Any settings made in the configuration file are ignored. -+-------+---------+-------------+--------------+ ++----------------------------------------------+ |command|Use: pipe|Type: string*|Default: unset| -+-------+---------+-------------+--------------+ ++----------------------------------------------+ This option need not be set when pipe is being used to deliver to pipes obtained directly from address redirections. In other cases, the option must be @@ -20884,41 +21230,41 @@ Exim, and each argument is separately expanded, as described in section 29.3 above. -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ |environment|Use: pipe|Type: string*|Default: unset| -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ This option is used to add additional variables to the environment in which the command runs (see section 29.4 for the default list). Its value is a string which is expanded, and then interpreted as a colon-separated list of environment settings of the form =. -+-------------+---------+------------+--------------+ ++---------------------------------------------------+ |escape_string|Use: pipe|Type: string|Default: unset| -+-------------+---------+------------+--------------+ ++---------------------------------------------------+ See check_string above. -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ |freeze_exec_fail|Use: pipe|Type: boolean|Default: false| -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ Failure to exec the command in a pipe transport is by default treated like any other failure while running the command. However, if freeze_exec_fail is set, failure to exec is treated specially, and causes the message to be frozen, whatever the setting of ignore_status. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |freeze_signal|Use: pipe|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ Normally if the process run by a command in a pipe transport exits on a signal, a bounce message is sent. If freeze_signal is set, the message will be frozen in Exim's queue instead. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |force_command|Use: pipe|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ Normally when a router redirects an address directly to a pipe command the command option on the transport is ignored. If force_command is set, the @@ -20932,9 +21278,9 @@ set, expanding out to the original argument vector as separate items, similarly to a Unix shell ""$@"" construct. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |ignore_status|Use: pipe|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ If this option is true, the status returned by the subprocess that is set up to run the command is ignored, and Exim behaves as if zero had been returned. @@ -20945,17 +21291,17 @@ Note: This option does not apply to timeouts, which do not return a status. See the timeout_defer option for how timeouts are handled. -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ |log_defer_output|Use: pipe|Type: boolean|Default: false| -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ If this option is set, and the status returned by the command is one of the codes listed in temp_errors (that is, delivery was deferred), and any output was produced, the first line of it is written to the main log. -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ |log_fail_output|Use: pipe|Type: boolean|Default: false| -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ If this option is set, and the command returns any output, and also ends with a return code that is neither zero nor one of the return codes listed in @@ -20963,17 +21309,17 @@ to the main log. This option and log_output are mutually exclusive. Only one of them may be set. -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ |log_output|Use: pipe|Type: boolean|Default: false| -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ If this option is set and the command returns any output, the first line of output is written to the main log, whatever the return code. This option and log_fail_output are mutually exclusive. Only one of them may be set. -+----------+---------+-------------+------------+ ++-----------------------------------------------+ |max_output|Use: pipe|Type: integer|Default: 20K| -+----------+---------+-------------+------------+ ++-----------------------------------------------+ This specifies the maximum amount of output that the command may produce on its standard output and standard error file combined. If the limit is exceeded, the @@ -20983,9 +21329,9 @@ return_output). Because of buffering effects, the amount of output may exceed the limit by a small amount before Exim notices. -+--------------+---------+-------------+------------------+ ++---------------------------------------------------------+ |message_prefix|Use: pipe|Type: string*|Default: see below| -+--------------+---------+-------------+------------------+ ++---------------------------------------------------------+ The string specified here is expanded and output at the start of every message. The default is unset if use_bsmtp is set. Otherwise it is @@ -21003,9 +21349,9 @@ Note: If you set use_crlf true, you must change any occurrences of "\n" to "\r\ n" in message_prefix. -+--------------+---------+-------------+------------------+ ++---------------------------------------------------------+ |message_suffix|Use: pipe|Type: string*|Default: see below| -+--------------+---------+-------------+------------------+ ++---------------------------------------------------------+ The string specified here is expanded and output at the end of every message. The default is unset if use_bsmtp is set. Otherwise it is a single newline. The @@ -21016,9 +21362,9 @@ Note: If you set use_crlf true, you must change any occurrences of "\n" to "\r\ n" in message_suffix. -+----+---------+------------+------------------+ ++----------------------------------------------+ |path|Use: pipe|Type: string|Default: see below| -+----+---------+------------+------------------+ ++----------------------------------------------+ This option specifies the string that is set up in the PATH environment variable of the subprocess. The default is: @@ -21029,9 +21375,9 @@ sought in the PATH directories, in the usual way. Warning: This does not apply to a command specified as a transport filter. -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ |permit_coredump|Use: pipe|Type: boolean|Default: false| -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ Normally Exim inhibits core-dumps during delivery. If you have a need to get a core-dump of a pipe command, enable this command. This enables core-dumps @@ -21042,9 +21388,9 @@ setuid binary and most operating systems will inhibit coredumps of these by default, so further OS-specific action may be required. -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ |pipe_as_creator|Use: pipe|Type: boolean|Default: false| -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ If the generic user option is not set and this option is true, the delivery process is run under the uid that was in force when Exim was originally called @@ -21052,9 +21398,9 @@ group option), the gid that was in force when Exim was originally called to accept the message is used. -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ |restrict_to_path|Use: pipe|Type: boolean|Default: false| -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ When this option is set, any command name not listed in allow_commands must contain no slashes. The command is searched for only in the directories listed @@ -21062,9 +21408,9 @@ command has been generated from a user's .forward file. This is usually handled by a pipe transport called address_pipe. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |return_fail_output|Use: pipe|Type: boolean|Default: false| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ If this option is true, and the command produced any output and ended with a return code other than zero or one of the codes listed in temp_errors (that is, @@ -21073,9 +21419,9 @@ from the command is discarded. This option and return_output are mutually exclusive. Only one of them may be set. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |return_output|Use: pipe|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ If this option is true, and the command produced any output, the delivery is deemed to have failed whatever the return code from the command, and the output @@ -21085,9 +21431,9 @@ option. This option and return_fail_output are mutually exclusive. Only one of them may be set. -+-----------+---------+-----------------+------------------+ ++----------------------------------------------------------+ |temp_errors|Use: pipe|Type: string list|Default: see below| -+-----------+---------+-----------------+------------------+ ++----------------------------------------------------------+ This option contains either a colon-separated list of numbers, or a single asterisk. If ignore_status is false and return_output is not set, and the @@ -21099,9 +21445,9 @@ that does not define these macros, it assumes values of 75 and 73, respectively. -+-------+---------+----------+-----------+ ++----------------------------------------+ |timeout|Use: pipe|Type: time|Default: 1h| -+-------+---------+----------+-----------+ ++----------------------------------------+ If the command fails to complete within this time, it is killed. This normally causes the delivery to fail (but see timeout_defer). A zero time interval @@ -21110,42 +21456,42 @@ and kills the whole process group on a timeout. However, this can be defeated if one of the processes starts a new process group. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |timeout_defer|Use: pipe|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ A timeout in a pipe transport, either in the command that the transport runs, or in a transport filter that is associated with it, is by default treated as a hard error, and the delivery fails. However, if timeout_defer is set true, both kinds of timeout become temporary errors, causing the delivery to be deferred. -+-----+---------+-------------------+------------+ ++------------------------------------------------+ |umask|Use: pipe|Type: octal integer|Default: 022| -+-----+---------+-------------------+------------+ ++------------------------------------------------+ This specifies the umask setting for the subprocess that runs the command. -+---------+---------+-------------+--------------+ ++------------------------------------------------+ |use_bsmtp|Use: pipe|Type: boolean|Default: false| -+---------+---------+-------------+--------------+ ++------------------------------------------------+ If this option is set true, the pipe transport writes messages in "batch SMTP" format, with the envelope sender and recipient(s) included as SMTP commands. If you want to include a leading HELO command with such messages, you can do so by -setting the message_prefix option. See section 47.10 for details of batch SMTP. +setting the message_prefix option. See section 48.10 for details of batch SMTP. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |use_classresources|Use: pipe|Type: boolean|Default: false| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ This option is available only when Exim is running on FreeBSD, NetBSD, or BSD/ OS. If it is set true, the setclassresources() function is used to set resource limits when a pipe transport is run to perform a delivery. The limits for the uid under which the pipe is to run are obtained from the login class database. -+--------+---------+-------------+--------------+ ++-----------------------------------------------+ |use_crlf|Use: pipe|Type: boolean|Default: false| -+--------+---------+-------------+--------------+ ++-----------------------------------------------+ This option causes lines to be terminated with the two-character CRLF sequence (carriage return, linefeed) instead of just a linefeed character. In the case @@ -21158,9 +21504,9 @@ and message_suffix end with a single linefeed, so their values must be changed to end with "\r\n" if use_crlf is set. -+---------+---------+-------------+--------------+ ++------------------------------------------------+ |use_shell|Use: pipe|Type: boolean|Default: false| -+---------+---------+-------------+--------------+ ++------------------------------------------------+ If this option is set, it causes the command to be passed to /bin/sh instead of being run directly from the transport, as described in section 29.3. This is @@ -21265,7 +21611,7 @@ run of the smtp transport over a single TCP/IP connection. (What Exim actually does when it has too many addresses to send in one message also depends on the value of the global remote_max_parallel option. Details are - given in section 47.1.) + given in section 48.1.) * When a message has been successfully delivered over a TCP/IP connection, Exim looks in its hints database to see if there are any other messages @@ -21312,9 +21658,9 @@ The private options of the smtp transport are as follows: -+----------------------------+---------+-------------+-------------+ ++------------------------------------------------------------------+ |address_retry_include_sender|Use: smtp|Type: boolean|Default: true| -+----------------------------+---------+-------------+-------------+ ++------------------------------------------------------------------+ When an address is delayed because of a 4xx response to a RCPT command, it is the combination of sender and recipient that is delayed in subsequent queue @@ -21323,9 +21669,9 @@ setting address_retry_include_sender false. However, this can lead to problems with servers that regularly issue 4xx responses to RCPT commands. -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ |allow_localhost|Use: smtp|Type: boolean|Default: false| -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ When a host specified in hosts or fallback_hosts (see below) turns out to be the local host, or is listed in hosts_treat_as_local, delivery is deferred by @@ -21334,9 +21680,9 @@ ensures that no looping will result (for example, a differently configured Exim is listening on the port to which the message is sent). -+--------------------+---------+-------------+--------------+ ++-----------------------------------------------------------+ |authenticated_sender|Use: smtp|Type: string*|Default: unset| -+--------------------+---------+-------------+--------------+ ++-----------------------------------------------------------+ When Exim has authenticated as a client, or if authenticated_sender_force is true, this option sets a value for the AUTH= item on outgoing MAIL commands, @@ -21365,25 +21711,25 @@ Because of expected uses such as that just described for Cyrus (when no domain is involved), there is no checking on the syntax of the provided value. -+--------------------------+---------+-------------+--------------+ ++-----------------------------------------------------------------+ |authenticated_sender_force|Use: smtp|Type: boolean|Default: false| -+--------------------------+---------+-------------+--------------+ ++-----------------------------------------------------------------+ If this option is set true, the authenticated_sender option's value is used for the AUTH= item on outgoing MAIL commands, even if Exim has not authenticated as a client. -+---------------+---------+----------+-----------+ ++------------------------------------------------+ |command_timeout|Use: smtp|Type: time|Default: 5m| -+---------------+---------+----------+-----------+ ++------------------------------------------------+ This sets a timeout for receiving a response to an SMTP command that has been sent out. It is also used when waiting for the initial banner line from the remote host. Its value must not be zero. -+---------------+---------+----------+-----------+ ++------------------------------------------------+ |connect_timeout|Use: smtp|Type: time|Default: 5m| -+---------------+---------+----------+-----------+ ++------------------------------------------------+ This sets a timeout for the connect() function, which sets up a TCP/IP call to a remote host. A setting of zero allows the system timeout (typically several @@ -21392,25 +21738,25 @@ no system timeout, which is why the default value for this option is 5 minutes, a value recommended by RFC 1123. -+-----------------------+---------+-------------+------------+ ++------------------------------------------------------------+ |connection_max_messages|Use: smtp|Type: integer|Default: 500| -+-----------------------+---------+-------------+------------+ ++------------------------------------------------------------+ This controls the maximum number of separate message deliveries that are sent over a single TCP/IP connection. If the value is zero, there is no limit. For testing purposes, this value can be overridden by the -oB command line option. -+------------+---------+----------+-----------+ ++---------------------------------------------+ |data_timeout|Use: smtp|Type: time|Default: 5m| -+------------+---------+----------+-----------+ ++---------------------------------------------+ This sets a timeout for the transmission of each block in the data portion of the message. As a result, the overall timeout for a message depends on the size of the message. Its value must not be zero. See also final_timeout. -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ |delay_after_cutoff|Use: smtp|Type: boolean|Default: true| -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ This option controls what happens when all remote IP addresses for a given domain have been inaccessible for so long that they have passed their retry @@ -21432,42 +21778,42 @@ continuous stream of messages for the dead hosts, unsetting delay_after_cutoff means that there will be many more attempts to deliver to them. -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ |dns_qualify_single|Use: smtp|Type: boolean|Default: true| -+------------------+---------+-------------+-------------+ ++--------------------------------------------------------+ If the hosts or fallback_hosts option is being used, and the gethostbyname option is false, the RES_DEFNAMES resolver option is set. See the qualify_single option in chapter 17 for more details. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |dns_search_parents|Use: smtp|Type: boolean|Default: false| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ If the hosts or fallback_hosts option is being used, and the gethostbyname option is false, the RES_DNSRCH resolver option is set. See the search_parents option in chapter 17 for more details. -+----------------------+---------+------------------+--------------+ ++------------------------------------------------------------------+ |dnssec_request_domains|Use: smtp|Type: domain list*|Default: unset| -+----------------------+---------+------------------+--------------+ ++------------------------------------------------------------------+ DNS lookups for domains matching dnssec_request_domains will be done with the -dnssec request bit set. This applies to all of the SRV, MX A6, AAAA, A lookup +dnssec request bit set. This applies to all of the SRV, MX, AAAA, A lookup sequence. -+----------------------+---------+------------------+--------------+ ++------------------------------------------------------------------+ |dnssec_require_domains|Use: smtp|Type: domain list*|Default: unset| -+----------------------+---------+------------------+--------------+ ++------------------------------------------------------------------+ DNS lookups for domains matching dnssec_request_domains will be done with the dnssec request bit set. Any returns not having the Authenticated Data bit (AD bit) set wil be ignored and logged as a host-lookup failure. This applies to -all of the SRV, MX A6, AAAA, A lookup sequence. +all of the SRV, MX, AAAA, A lookup sequence. -+----+---------+-------------+--------------+ ++-------------------------------------------+ |dscp|Use: smtp|Type: string*|Default: unset| -+----+---------+-------------+--------------+ ++-------------------------------------------+ This option causes the DSCP value associated with a socket to be set to one of a number of fixed strings or to numeric value. The -bI:dscp option may be used @@ -21481,9 +21827,9 @@ equipment, or do much of anything without cooperation with your Network Engineer and those of all network operators between the source and destination. -+--------------+---------+-----------------+--------------+ ++---------------------------------------------------------+ |fallback_hosts|Use: smtp|Type: string list|Default: unset| -+--------------+---------+-----------------+--------------+ ++---------------------------------------------------------+ String expansion is not applied to this option. The argument must be a colon-separated list of host names or IP addresses, optionally also including @@ -21514,16 +21860,16 @@ cases when the host list comes with the address and when it is taken from hosts . This option provides a "use a smart host only if delivery fails" facility. -+-------------+---------+----------+------------+ ++-----------------------------------------------+ |final_timeout|Use: smtp|Type: time|Default: 10m| -+-------------+---------+----------+------------+ ++-----------------------------------------------+ This is the timeout that applies while waiting for the response to the final line containing just "." that terminates a message. Its value must not be zero. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |gethostbyname|Use: smtp|Type: boolean|Default: false| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ If this option is true when the hosts and/or fallback_hosts options are being used, names are looked up using gethostbyname() (or getipnodebyname() when @@ -21531,17 +21877,17 @@ the DNS, but it may also consult other sources of information such as /etc/ hosts. -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ |gnutls_compat_mode|Use: smtp|Type: boolean|Default: unset| -+------------------+---------+-------------+--------------+ ++---------------------------------------------------------+ This option controls whether GnuTLS is used in compatibility mode in an Exim server. This reduces security slightly, but improves interworking with older implementations of TLS. -+---------+---------+-------------+------------------+ ++----------------------------------------------------+ |helo_data|Use: smtp|Type: string*|Default: see below| -+---------+---------+-------------+------------------+ ++----------------------------------------------------+ The value of this option is expanded after a connection to a another host has been set up. The result is used as the argument for the EHLO, HELO, or LHLO @@ -21563,9 +21909,9 @@ The use of helo_data applies both to sending messages and when doing callouts. -+-----+---------+------------------+--------------+ ++-------------------------------------------------+ |hosts|Use: smtp|Type: string list*|Default: unset| -+-----+---------+------------------+--------------+ ++-------------------------------------------------+ Hosts are associated with an address by a router such as dnslookup, which finds the hosts by looking up the address domain in the DNS, or by manualroute, which @@ -21596,9 +21942,9 @@ During delivery, the hosts are tried in order, subject to their retry status, unless hosts_randomize is set. -+-----------------+---------+----------------+--------------+ ++-----------------------------------------------------------+ |hosts_avoid_esmtp|Use: smtp|Type: host list*|Default: unset| -+-----------------+---------+----------------+--------------+ ++-----------------------------------------------------------+ This option is for use with broken hosts that announce ESMTP facilities (for example, PIPELINING) and then fail to implement them properly. When a host @@ -21606,63 +21952,62 @@ SMTP session. This means that it cannot use any of the ESMTP facilities such as AUTH, PIPELINING, SIZE, and STARTTLS. -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ |hosts_avoid_pipelining|Use: smtp|Type: host list*|Default: unset| -+----------------------+---------+----------------+--------------+ ++----------------------------------------------------------------+ Exim will not use the SMTP PIPELINING extension when delivering to any host that matches this list, even if the server host advertises PIPELINING support. -+---------------+---------+----------------+--------------+ ++---------------------------------------------------------+ |hosts_avoid_tls|Use: smtp|Type: host list*|Default: unset| -+---------------+---------+----------------+--------------+ ++---------------------------------------------------------+ Exim will not try to start a TLS session when delivering to any host that -matches this list. See chapter 41 for details of TLS. +matches this list. See chapter 42 for details of TLS. -+----------------------+---------+----------------+----------+ -|hosts_verify_avoid_tls|Use: smtp|Type: host list*|Default: *| -+----------------------+---------+----------------+----------+ ++----------------------------------------------------------------+ +|hosts_verify_avoid_tls|Use: smtp|Type: host list*|Default: unset| ++----------------------------------------------------------------+ Exim will not try to start a TLS session for a verify callout, or when -delivering in cutthrough mode, to any host that matches this list. Note that -the default is to not use TLS. +delivering in cutthrough mode, to any host that matches this list. -+-------------+---------+-------------+----------+ ++------------------------------------------------+ |hosts_max_try|Use: smtp|Type: integer|Default: 5| -+-------------+---------+-------------+----------+ ++------------------------------------------------+ This option limits the number of IP addresses that are tried for any one delivery in cases where there are temporary delivery errors. Section 30.5 describes in detail how the value of this option is used. -+-----------------------+---------+-------------+-----------+ ++-----------------------------------------------------------+ |hosts_max_try_hardlimit|Use: smtp|Type: integer|Default: 50| -+-----------------------+---------+-------------+-----------+ ++-----------------------------------------------------------+ This is an additional check on the maximum number of IP addresses that Exim tries for any one delivery. Section 30.5 describes its use and why it exists. -+----------------+---------+----------------+--------------+ ++----------------------------------------------------------+ |hosts_nopass_tls|Use: smtp|Type: host list*|Default: unset| -+----------------+---------+----------------+--------------+ ++----------------------------------------------------------+ For any host that matches this list, a connection on which a TLS session has been started will not be passed to a new delivery process for sending another -message on the same connection. See section 41.11 for an explanation of when +message on the same connection. See section 42.11 for an explanation of when this might be needed. -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ |hosts_override|Use: smtp|Type: boolean|Default: false| -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ If this option is set and the hosts option is also set, any hosts that are attached to the address are ignored, and instead the hosts specified by the hosts option are always used. This option does not apply to fallback_hosts. -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ |hosts_randomize|Use: smtp|Type: boolean|Default: false| -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ If this option is set, and either the list of hosts is taken from the hosts or the fallback_hosts option, or the hosts supplied by the router were not @@ -21682,9 +22027,9 @@ randomized for each use, but the first three always end up before the last two. If hosts_randomize is not set, a "+" item in the list is ignored. -+------------------+---------+----------------+--------------+ ++------------------------------------------------------------+ |hosts_require_auth|Use: smtp|Type: host list*|Default: unset| -+------------------+---------+----------------+--------------+ ++------------------------------------------------------------+ This option provides a list of servers for which authentication must succeed before Exim will try to transfer a message. If authentication fails for servers @@ -21694,34 +22039,34 @@ hard failure if required. See also hosts_try_auth, and chapter 33 for details of authentication. -+------------------+---------+----------------+----------+ ++--------------------------------------------------------+ |hosts_request_ocsp|Use: smtp|Type: host list*|Default: *| -+------------------+---------+----------------+----------+ ++--------------------------------------------------------+ Exim will request a Certificate Status on a TLS session for any host that matches this list. tls_verify_certificates should also be set for the transport. -+------------------+---------+----------------+--------------+ ++------------------------------------------------------------+ |hosts_require_ocsp|Use: smtp|Type: host list*|Default: unset| -+------------------+---------+----------------+--------------+ ++------------------------------------------------------------+ Exim will request, and check for a valid Certificate Status being given, on a TLS session for any host that matches this list. tls_verify_certificates should also be set for the transport. -+-----------------+---------+----------------+--------------+ ++-----------------------------------------------------------+ |hosts_require_tls|Use: smtp|Type: host list*|Default: unset| -+-----------------+---------+----------------+--------------+ ++-----------------------------------------------------------+ Exim will insist on using a TLS session when delivering to any host that -matches this list. See chapter 41 for details of TLS. Note: This option affects +matches this list. See chapter 42 for details of TLS. Note: This option affects outgoing mail only. To insist on TLS for incoming messages, use an appropriate ACL. -+--------------+---------+----------------+--------------+ ++--------------------------------------------------------+ |hosts_try_auth|Use: smtp|Type: host list*|Default: unset| -+--------------+---------+----------------+--------------+ ++--------------------------------------------------------+ This option provides a list of servers to which, provided they announce authentication support, Exim will attempt to authenticate as a client when it @@ -21729,16 +22074,17 @@ unauthenticated. See also hosts_require_auth, and chapter 33 for details of authentication. -+--------------+---------+----------------+--------------+ -|hosts_try_prdr|Use: smtp|Type: host list*|Default: unset| -+--------------+---------+----------------+--------------+ ++----------------------------------------------------+ +|hosts_try_prdr|Use: smtp|Type: host list*|Default: *| ++----------------------------------------------------+ This option provides a list of servers to which, provided they announce PRDR -support, Exim will attempt to negotiate PRDR for multi-recipient messages. +support, Exim will attempt to negotiate PRDR for multi-recipient messages. The +option can usually be left as default. -+---------+---------+------------------+--------------+ ++-----------------------------------------------------+ |interface|Use: smtp|Type: string list*|Default: unset| -+---------+---------+------------------+--------------+ ++-----------------------------------------------------+ This option specifies which interface to bind to when making an outgoing SMTP call. The value is an IP address, not an interface name such as "eth0". Do not @@ -21763,9 +22109,9 @@ interface is not set, or is ignored, the system's IP functions choose which interface to use if the host has more than one. -+---------+---------+-------------+-------------+ ++-----------------------------------------------+ |keepalive|Use: smtp|Type: boolean|Default: true| -+---------+---------+-------------+-------------+ ++-----------------------------------------------+ This option controls the setting of SO_KEEPALIVE on outgoing TCP/IP socket connections. When set, it causes the kernel to probe idle connections @@ -21777,26 +22123,26 @@ call properly. The keepalive mechanism takes several hours to detect unreachable hosts. -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ |lmtp_ignore_quota|Use: smtp|Type: boolean|Default: false| -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ If this option is set true when the protocol option is set to "lmtp", the string "IGNOREQUOTA" is added to RCPT commands, provided that the LMTP server has advertised support for IGNOREQUOTA in its response to the LHLO command. -+--------+---------+-------------+------------+ ++---------------------------------------------+ |max_rcpt|Use: smtp|Type: integer|Default: 100| -+--------+---------+-------------+------------+ ++---------------------------------------------+ This option limits the number of RCPT commands that are sent in a single SMTP message transaction. Each set of addresses is treated independently, and so can cause parallel connections to the same host if remote_max_parallel permits this. -+------------+---------+-------------+-------------+ -|multi_domain|Use: smtp|Type: boolean|Default: true| -+------------+---------+-------------+-------------+ ++---------------------------------------------------+ +|multi_domain|Use: smtp|Type: boolean*|Default: true| ++---------------------------------------------------+ When this option is set, the smtp transport can handle a number of addresses containing a mixture of different domains provided they all resolve to the same @@ -21805,9 +22151,12 @@ for the transport, because it is set only when there is a single domain involved in a remote delivery. -+----+---------+-------------+------------------+ +It is expanded per-address and can depend on any of $address_data, $domain_data +, $local_part_data, $host, $host_address and $host_port. + ++-----------------------------------------------+ |port|Use: smtp|Type: string*|Default: see below| -+----+---------+-------------+------------------+ ++-----------------------------------------------+ This option specifies the TCP/IP port on the server to which Exim connects. Note: Do not confuse this with the port that was used when a message was @@ -21820,9 +22169,9 @@ "smtp", but if protocol is set to "lmtp", the default is "lmtp". If the expansion fails, or if a port number cannot be found, delivery is deferred. -+--------+---------+------------+-------------+ ++---------------------------------------------+ |protocol|Use: smtp|Type: string|Default: smtp| -+--------+---------+------------+-------------+ ++---------------------------------------------+ If this option is set to "lmtp" instead of "smtp", the default value for the port option changes to "lmtp", and the transport operates the LMTP protocol @@ -21835,9 +22184,9 @@ outbound SSL-on-connect, instead of using STARTTLS to upgrade. The Internet standards bodies strongly discourage use of this mode. -+------------------------+---------+-------------+-------------+ -|retry_include_ip_address|Use: smtp|Type: boolean|Default: true| -+------------------------+---------+-------------+-------------+ ++---------------------------------------------------------------+ +|retry_include_ip_address|Use: smtp|Type: boolean*|Default: true| ++---------------------------------------------------------------+ Exim normally includes both the host name and the IP address in the key it constructs for indexing retry data after a temporary delivery failure. This @@ -21848,12 +22197,13 @@ However, in some dialup environments hosts are assigned a different IP address each time they connect. In this situation the use of the IP address as part of the retry key leads to undesirable behaviour. Setting this option false causes -Exim to use only the host name. This should normally be done on a separate -instance of the smtp transport, set up specially to handle the dialup hosts. +Exim to use only the host name. -+---------------+---------+----------------+--------------+ +Since it is expanded it can be made to depend on the host or domain. + ++---------------------------------------------------------+ |serialize_hosts|Use: smtp|Type: host list*|Default: unset| -+---------------+---------+----------------+--------------+ ++---------------------------------------------------------+ Because Exim operates in a distributed manner, if several messages for the same host arrive at around the same time, more than one simultaneous connection to @@ -21874,9 +22224,9 @@ or two files, depending on the type of DBM in use. The same files are used for ETRN serialization. -+-------------+---------+-------------+-------------+ ++---------------------------------------------------+ |size_addition|Use: smtp|Type: integer|Default: 1024| -+-------------+---------+-------------+-------------+ ++---------------------------------------------------+ If a remote SMTP server indicates that it supports the SIZE option of the MAIL command, Exim uses this to pass over the message size at the start of an SMTP @@ -21888,14 +22238,14 @@ Alternatively, if the value of size_addition is set negative, it disables the use of the SIZE option altogether. -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ |tls_certificate|Use: smtp|Type: string*|Default: unset| -+---------------+---------+-------------+--------------+ ++------------------------------------------------------+ The value of this option must be the absolute path to a file which contains the client's certificate, for possible use when sending a message over an encrypted connection. The values of $host and $host_address are set to the name and -address of the server during the expansion. See chapter 41 for details of TLS. +address of the server during the expansion. See chapter 42 for details of TLS. Note: This option must be set if you want Exim to be able to use a TLS certificate when sending messages as a client. The global option of the same @@ -21903,16 +22253,16 @@ assumed that the same certificate should be used when Exim is operating as a client. -+-------+---------+-------------+--------------+ ++----------------------------------------------+ |tls_crl|Use: smtp|Type: string*|Default: unset| -+-------+---------+-------------+--------------+ ++----------------------------------------------+ This option specifies a certificate revocation list. The expanded value must be the name of a file that contains a CRL in PEM format. -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ |tls_dh_min_bits|Use: smtp|Type: integer|Default: 1024| -+---------------+---------+-------------+-------------+ ++-----------------------------------------------------+ When establishing a TLS session, if a ciphersuite which uses Diffie-Hellman key agreement is negotiated, the server will provide a large prime number for use. @@ -21921,9 +22271,9 @@ Only supported when using GnuTLS. -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ |tls_privatekey|Use: smtp|Type: string*|Default: unset| -+--------------+---------+-------------+--------------+ ++-----------------------------------------------------+ The value of this option must be the absolute path to a file which contains the client's private key. This is used when sending a message over an encrypted @@ -21931,37 +22281,37 @@ are set to the name and address of the server during the expansion. If this option is unset, or the expansion is forced to fail, or the result is an empty string, the private key is assumed to be in the same file as the certificate. -See chapter 41 for details of TLS. +See chapter 42 for details of TLS. -+-------------------+---------+-------------+--------------+ ++----------------------------------------------------------+ |tls_require_ciphers|Use: smtp|Type: string*|Default: unset| -+-------------------+---------+-------------+--------------+ ++----------------------------------------------------------+ The value of this option must be a list of permitted cipher suites, for use when setting up an outgoing encrypted connection. (There is a global option of the same name for controlling incoming connections.) The values of $host and $host_address are set to the name and address of the server during the -expansion. See chapter 41 for details of TLS; note that this option is used in -different ways by OpenSSL and GnuTLS (see sections 41.4 and 41.5). For GnuTLS, +expansion. See chapter 42 for details of TLS; note that this option is used in +different ways by OpenSSL and GnuTLS (see sections 42.4 and 42.5). For GnuTLS, the order of the ciphers is a preference order. -+-------+---------+-------------+--------------+ ++----------------------------------------------+ |tls_sni|Use: smtp|Type: string*|Default: unset| -+-------+---------+-------------+--------------+ ++----------------------------------------------+ If this option is set then it sets the $tls_out_sni variable and causes any TLS session to pass this value as the Server Name Indication extension to the remote side, which can be used by the remote side to select an appropriate certificate and private key for the session. -See 41.10 for more information. +See 42.10 for more information. Note that for OpenSSL, this feature requires a build of OpenSSL that supports TLS extensions. -+---------------------+---------+-------------+-------------+ ++-----------------------------------------------------------+ |tls_tempfail_tryclear|Use: smtp|Type: boolean|Default: true| -+---------------------+---------+-------------+-------------+ ++-----------------------------------------------------------+ When the server host is not in hosts_require_tls, and there is a problem in setting up a TLS session, this option determines whether or not Exim should try @@ -21972,37 +22322,58 @@ fails, Exim closes the current connection (because it is in an unknown state), opens a new one to the same host, and then tries the delivery in clear. -+--------------------+---------+----------------------+--------+ -|tls_try_verify_hosts|Use: smtp|Type: host list* unset|Default:| -+--------------------+---------+----------------------+--------+ ++----------------------------------------------------------+ +|tls_try_verify_hosts|Use: smtp|Type: host list*|Default: *| ++----------------------------------------------------------+ This option gives a list of hosts for which, on encrypted connections, certificate verification will be tried but need not succeed. The tls_verify_certificates option must also be set. Note that unless the host is in this list TLS connections will be denied to hosts using self-signed -certificates when tls_verify_certificates is set. The +certificates when tls_verify_certificates is matched. The $tls_out_certificate_verified variable is set when certificate verification succeeds. -+-----------------------+---------+-------------+--------------+ -|tls_verify_certificates|Use: smtp|Type: string*|Default: unset| -+-----------------------+---------+-------------+--------------+ - -The value of this option must be the absolute path to a file containing -permitted server certificates, for use when setting up an encrypted connection. -Alternatively, if you are using OpenSSL, you can set tls_verify_certificates to -the name of a directory containing certificate files. This does not work with -GnuTLS; the option must be set to the name of a single file if you are using -GnuTLS. The values of $host and $host_address are set to the name and address -of the server during the expansion of this option. See chapter 41 for details -of TLS. ++---------------------------------------------------------------+ +|tls_verify_cert_hostnames|Use: smtp|Type: host list*|Default: *| ++---------------------------------------------------------------+ + +This option give a list of hosts for which, while verifying the server +certificate, checks will be included on the host name (note that this will +generally be the result of a DNS MX lookup) versus Subject and +Subject-Alternate-Name fields. Wildcard names are permitted limited to being +the initial component of a 3-or-more component FQDN. + +There is no equivalent checking on client certificates. + ++---------------------------------------------------------------+ +|tls_verify_certificates|Use: smtp|Type: string*|Default: system| ++---------------------------------------------------------------+ + +The value of this option must be either the word "system" or the absolute path +to a file or directory containing permitted certificates for servers, for use +when setting up an encrypted connection. + +The "system" value for the option will use a location compiled into the SSL +library. This is not available for GnuTLS versions preceding 3.0.20; a value of +"system" is taken as empty and an explicit location must be specified. + +The use of a directory for the option value is not avilable for GnuTLS versions +preceding 3.3.6 and a single file must be used. + +With OpenSSL the certificates specified explicitly either by file or directory +are added to those given by the system default location. + +The values of $host and $host_address are set to the name and address of the +server during the expansion of this option. See chapter 42 for details of TLS. For back-compatability, if neither tls_verify_hosts nor tls_try_verify_hosts -are set and certificate verification fails the TLS connection is closed. +are set (a single-colon empty list counts as being set) and certificate +verification fails the TLS connection is closed. -+----------------+---------+----------------------+--------+ -|tls_verify_hosts|Use: smtp|Type: host list* unset|Default:| -+----------------+---------+----------------------+--------+ ++----------------------------------------------------------+ +|tls_verify_hosts|Use: smtp|Type: host list*|Default: unset| ++----------------------------------------------------------+ This option gives a list of hosts for which. on encrypted connections, certificate verification must succeed. The tls_verify_certificates option must @@ -22527,7 +22898,7 @@ been delayed, delivery of a new message to the same host is not immediately tried, but waits for the host's retry time to arrive. If the retry_defer log selector is set, the message "retry time not reached" is written to the main -log whenever a delivery is skipped for this reason. Section 47.2 contains more +log whenever a delivery is skipped for this reason. Section 48.2 contains more details of the handling of errors during remote deliveries. Retry processing applies to routing as well as to delivering, except as covered @@ -22712,6 +23083,13 @@ legitimate reasons for this (host died, network died), but if it repeats a lot for the same host, it indicates something odd. +lookup + + A DNS lookup for a host failed. Note that a dnslookup router will need to + have matched its fail_defer_domains option for this retry type to be + usable. Also note that a manualroute router will probably need its + host_find_failed option set to defer. + refused_MX A connection to a host obtained from an MX record was refused. @@ -22892,7 +23270,7 @@ sending everything to a smart host, for example). The data in the retry hints database can be inspected by using the exim_dumpdb -or exim_fixdb utility programs (see chapter 52). The latter utility can also be +or exim_fixdb utility programs (see chapter 53). The latter utility can also be used to change the data. The exinext utility script can be used to find out what the next retry times are for the hosts associated with a particular mail domain, and also for local deliveries that have been deferred. @@ -23018,7 +23396,7 @@ Two exceptional actions are applied to prevent this happening. The first applies to errors that are related to a message rather than a remote host. -Section 47.2 has a discussion of the different kinds of error; examples of +Section 48.2 has a discussion of the different kinds of error; examples of message-related errors are 4xx responses to MAIL or DATA commands, and quota failures. For this type of error, if a message's arrival time is earlier than the "first failed" time for the error, the earlier time is used when scanning @@ -23100,6 +23478,7 @@ AUTH_HEIMDAL_GSSAPI=yes AUTH_PLAINTEXT=yes AUTH_SPA=yes +AUTH_TLS=yes in Local/Makefile, respectively. The first of these supports the CRAM-MD5 authentication mechanism (RFC 2195), and the second provides an interface to @@ -23113,6 +23492,9 @@ several MUAs. The seventh authenticator supports Microsoft's Secure Password Authentication mechanism. +The eighth is an Exim authenticator but not an SMTP one; instead it can use +information from a TLS negotiation. + The authenticators are configured using the same syntax as other drivers (see section 6.22). If no authenticators are required, no authentication section need be present in the configuration file. Each authenticator can in principle @@ -23168,9 +23550,9 @@ 33.1 Generic options for authenticators --------------------------------------- -+----------------+-------------------+-------------+--------------+ ++-----------------------------------------------------------------+ |client_condition|Use: authenticators|Type: string*|Default: unset| -+----------------+-------------------+-------------+--------------+ ++-----------------------------------------------------------------+ When Exim is authenticating as a client, it skips any authenticator whose client_condition expansion yields "0", "no", or "false". This can be used, for @@ -23179,24 +23561,24 @@ client_condition = ${if !eq{$tls_out_cipher}{}} -+-------------+-------------------+-------------+--------------+ ++--------------------------------------------------------------+ |client_set_id|Use: authenticators|Type: string*|Default: unset| -+-------------+-------------------+-------------+--------------+ ++--------------------------------------------------------------+ When client authentication succeeds, this condition is expanded; the result is used in the log lines for outbound messasges. Typically it will be the user name used for authentication. -+------+-------------------+------------+--------------+ ++------------------------------------------------------+ |driver|Use: authenticators|Type: string|Default: unset| -+------+-------------------+------------+--------------+ ++------------------------------------------------------+ This option must always be set. It specifies which of the available authenticators is to be used. -+-----------+-------------------+------------+--------------+ ++-----------------------------------------------------------+ |public_name|Use: authenticators|Type: string|Default: unset| -+-----------+-------------------+------------+--------------+ ++-----------------------------------------------------------+ This option specifies the name of the authentication mechanism that the driver implements, and by which it is known to the outside world. These names should @@ -23204,9 +23586,9 @@ but Exim in fact matches them caselessly. If public_name is not set, it defaults to the driver's instance name. -+--------------------------+-------------------+-------------+--------------+ ++---------------------------------------------------------------------------+ |server_advertise_condition|Use: authenticators|Type: string*|Default: unset| -+--------------------------+-------------------+-------------+--------------+ ++---------------------------------------------------------------------------+ When a server is about to advertise an authentication mechanism, the condition is expanded. If it yields the empty string, "0", "no", or "false", the @@ -23214,9 +23596,9 @@ advertised. If the failure was not forced, and was not caused by a lookup defer, the incident is logged. See section 33.3 below for further discussion. -+----------------+-------------------+-------------+--------------+ ++-----------------------------------------------------------------+ |server_condition|Use: authenticators|Type: string*|Default: unset| -+----------------+-------------------+-------------+--------------+ ++-----------------------------------------------------------------+ This option must be set for a plaintext server authenticator, where it is used directly to control authentication. See section 34.2 for details. @@ -23235,9 +23617,9 @@ result, a temporary error code is returned, with the expanded string as the error text. -+------------------+-------------------+-------------+--------------+ ++-------------------------------------------------------------------+ |server_debug_print|Use: authenticators|Type: string*|Default: unset| -+------------------+-------------------+-------------+--------------+ ++-------------------------------------------------------------------+ If this option is set and authentication debugging is enabled (see the -d command line option), the string is expanded and included in the debugging @@ -23245,9 +23627,9 @@ out the values of variables. If expansion of the string fails, the error message is written to the debugging output, and Exim carries on processing. -+-------------+-------------------+-------------+--------------+ ++--------------------------------------------------------------+ |server_set_id|Use: authenticators|Type: string*|Default: unset| -+-------------+-------------------+-------------+--------------+ ++--------------------------------------------------------------+ When an Exim server successfully authenticates a client, this string is expanded using data from the authentication, and preserved for any incoming @@ -23257,9 +23639,9 @@ refer to it subsequently during delivery of the message. If expansion fails, the option is ignored. -+--------------------------+-------------------+-------------+--------------+ ++---------------------------------------------------------------------------+ |server_mail_auth_condition|Use: authenticators|Type: string*|Default: unset| -+--------------------------+-------------------+-------------+--------------+ ++---------------------------------------------------------------------------+ This option allows a server to discard authenticated sender addresses supplied as part of MAIL commands in SMTP connections that are authenticated by the @@ -23479,7 +23861,7 @@ authentication mechanisms, both of which transfer authentication data as plain (unencrypted) text (though base64 encoded). The use of plain text is a security risk; you are strongly advised to insist on the use of SMTP encryption (see -chapter 41) if you use the PLAIN or LOGIN mechanisms. If you do use unencrypted +chapter 42) if you use the PLAIN or LOGIN mechanisms. If you do use unencrypted plain text, you should not use the same passwords for SMTP connections as you do for login accounts. @@ -23489,16 +23871,16 @@ When configured as a server, plaintext uses the following options: -+----------------+-------------------+-------------+--------------+ ++-----------------------------------------------------------------+ |server_condition|Use: authenticators|Type: string*|Default: unset| -+----------------+-------------------+-------------+--------------+ ++-----------------------------------------------------------------+ This is actually a global authentication option, but it must be set in order to configure the plaintext driver as a server. Its use is described below. -+--------------+--------------+-------------+--------------+ ++----------------------------------------------------------+ |server_prompts|Use: plaintext|Type: string*|Default: unset| -+--------------+--------------+-------------+--------------+ ++----------------------------------------------------------+ The contents of this option, after expansion, must be a colon-separated list of prompt strings. If expansion fails, a temporary authentication rejection is @@ -23682,18 +24064,18 @@ The plaintext authenticator has two client options: -+----------------------------+--------------+-------------+--------------+ ++------------------------------------------------------------------------+ |client_ignore_invalid_base64|Use: plaintext|Type: boolean|Default: false| -+----------------------------+--------------+-------------+--------------+ ++------------------------------------------------------------------------+ If the client receives a server prompt that is not a valid base64 string, authentication is abandoned by default. However, if this option is set true, the error in the challenge is ignored and the client sends the response as usual. -+-----------+--------------+-------------+--------------+ ++-------------------------------------------------------+ |client_send|Use: plaintext|Type: string*|Default: unset| -+-----------+--------------+-------------+--------------+ ++-------------------------------------------------------+ The string is a colon-separated list of authentication data strings. Each string is independently expanded before being sent to the server. The first @@ -23755,9 +24137,9 @@ This authenticator has one server option, which must be set to configure the authenticator as a server: -+-------------+-------------+-------------+--------------+ ++--------------------------------------------------------+ |server_secret|Use: cram_md5|Type: string*|Default: unset| -+-------------+-------------+-------------+--------------+ ++--------------------------------------------------------+ When the server receives the client's response, the user name is placed in the expansion variable $auth1, and server_secret is expanded to obtain the password @@ -23805,7 +24187,7 @@ driver = cram_md5 public_name = CRAM-MD5 server_secret = ${lookup{$auth1:mail.example.org:userPassword}\ - dbmjz{/etc/sasldb2}} + dbmjz{/etc/sasldb2}{$value}fail} server_set_id = $auth1 @@ -23814,16 +24196,16 @@ When used as a client, the cram_md5 authenticator has two options: -+-----------+-------------+-------------+------------------------------+ ++----------------------------------------------------------------------+ |client_name|Use: cram_md5|Type: string*|Default: the primary host name| -+-----------+-------------+-------------+------------------------------+ ++----------------------------------------------------------------------+ This string is expanded, and the result used as the user name data when computing the response to the server's challenge. -+-------------+-------------+-------------+--------------+ ++--------------------------------------------------------+ |client_secret|Use: cram_md5|Type: string*|Default: unset| -+-------------+-------------+-------------+--------------+ ++--------------------------------------------------------+ This option must be set for the authenticator to work as a client. Its value is expanded and the result used as the secret string when computing the response. @@ -23894,17 +24276,17 @@ variable for this purpose is now deprecated, as it can lead to confusion in string expansions that also use numeric variables for other things. -+---------------+---------------+-------------+------------------+ ++----------------------------------------------------------------+ |server_hostname|Use: cyrus_sasl|Type: string*|Default: see below| -+---------------+---------------+-------------+------------------+ ++----------------------------------------------------------------+ This option selects the hostname that is used when communicating with the library. The default value is "$primary_hostname". It is up to the underlying SASL plug-in what it does with this data. -+-----------+---------------+------------+------------------+ ++-----------------------------------------------------------+ |server_mech|Use: cyrus_sasl|Type: string|Default: see below| -+-----------+---------------+------------+------------------+ ++-----------------------------------------------------------+ This option selects the authentication mechanism this driver should use. The default is the value of the generic public_name option. This option allows you @@ -23916,15 +24298,15 @@ server_mech = CRAM-MD5 server_set_id = $auth1 -+------------+---------------+-------------+--------------+ ++---------------------------------------------------------+ |server_realm|Use: cyrus_sasl|Type: string*|Default: unset| -+------------+---------------+-------------+--------------+ ++---------------------------------------------------------+ This specifies the SASL realm that the server claims to be in. -+--------------+---------------+------------+---------------+ ++-----------------------------------------------------------+ |server_service|Use: cyrus_sasl|Type: string|Default: "smtp"| -+--------------+---------------+------------+---------------+ ++-----------------------------------------------------------+ This is the SASL service that the server claims to implement. @@ -23954,16 +24336,14 @@ This authenticator is an interface to the authentication facility of the Dovecot POP/IMAP server, which can support a number of authentication methods. +Note that Dovecot must be configured to use auth-client not auth-userdb. If you +are using Dovecot to authenticate POP/IMAP clients, it might be helpful to use +the same mechanisms for SMTP authentication. This is a server authenticator +only. There is only one option: -Note that Dovecot must be configured to use auth-client not auth-userdb. - -If you are using Dovecot to authenticate POP/IMAP clients, it might be helpful -to use the same mechanisms for SMTP authentication. This is a server -authenticator only. There is only one option: - -+-------------+------------+------------+--------------+ ++------------------------------------------------------+ |server_socket|Use: dovecot|Type: string|Default: unset| -+-------------+------------+------------+--------------+ ++------------------------------------------------------+ This option must specify the socket that is the interface to Dovecot authentication. The public_name option must specify an authentication mechanism @@ -24001,9 +24381,9 @@ particular new authentication mechanism will be supported without code changes in Exim. -+---------------------+----------+-------------+--------------+ ++-------------------------------------------------------------+ |server_channelbinding|Use: gsasl|Type: boolean|Default: false| -+---------------------+----------+-------------+--------------+ ++-------------------------------------------------------------+ Some authentication mechanisms are able to use external context at both ends of the session to bind the authentication to that context, and fail the @@ -24023,17 +24403,17 @@ option causes some clients to start failing. Some future release of Exim may switch the default to be true. -+---------------+----------+-------------+------------------+ ++-----------------------------------------------------------+ |server_hostname|Use: gsasl|Type: string*|Default: see below| -+---------------+----------+-------------+------------------+ ++-----------------------------------------------------------+ This option selects the hostname that is used when communicating with the library. The default value is "$primary_hostname". Some mechanisms will use this data. -+-----------+----------+------------+------------------+ ++------------------------------------------------------+ |server_mech|Use: gsasl|Type: string|Default: see below| -+-----------+----------+------------+------------------+ ++------------------------------------------------------+ This option selects the authentication mechanism this driver should use. The default is the value of the generic public_name option. This option allows you @@ -24045,9 +24425,9 @@ server_mech = CRAM-MD5 server_set_id = $auth1 -+---------------+----------+-------------+--------------+ ++-------------------------------------------------------+ |server_password|Use: gsasl|Type: string*|Default: unset| -+---------------+----------+-------------+--------------+ ++-------------------------------------------------------+ Various mechanisms need access to the cleartext password on the server, so that proof-of-possession can be demonstrated on the wire, without sending the @@ -24063,30 +24443,30 @@ If using this option, it may make sense to set the server_condition option to be simply "true". -+------------+----------+-------------+--------------+ ++----------------------------------------------------+ |server_realm|Use: gsasl|Type: string*|Default: unset| -+------------+----------+-------------+--------------+ ++----------------------------------------------------+ This specifies the SASL realm that the server claims to be in. Some mechanisms will use this data. -+-----------------+----------+-------------+--------------+ ++---------------------------------------------------------+ |server_scram_iter|Use: gsasl|Type: string*|Default: unset| -+-----------------+----------+-------------+--------------+ ++---------------------------------------------------------+ This option provides data for the SCRAM family of mechanisms. $auth1 is not available at evaluation time. (This may change, as we receive feedback on use) -+-----------------+----------+-------------+--------------+ ++---------------------------------------------------------+ |server_scram_salt|Use: gsasl|Type: string*|Default: unset| -+-----------------+----------+-------------+--------------+ ++---------------------------------------------------------+ This option provides data for the SCRAM family of mechanisms. $auth1 is not available at evaluation time. (This may change, as we receive feedback on use) -+--------------+----------+------------+---------------+ ++------------------------------------------------------+ |server_service|Use: gsasl|Type: string|Default: "smtp"| -+--------------+----------+------------+---------------+ ++------------------------------------------------------+ This is the SASL service that the server claims to implement. Some mechanisms will use this data. @@ -24142,25 +24522,25 @@ The heimdal_gssapi authenticator provides server integration for the Heimdal GSSAPI/Kerberos library, permitting Exim to set a keytab pathname reliably. -+---------------+-------------------+-------------+------------------+ ++--------------------------------------------------------------------+ |server_hostname|Use: heimdal_gssapi|Type: string*|Default: see below| -+---------------+-------------------+-------------+------------------+ ++--------------------------------------------------------------------+ This option selects the hostname that is used, with server_service, for constructing the GSS server name, as a GSS_C_NT_HOSTBASED_SERVICE identifier. The default value is "$primary_hostname". -+-------------+-------------------+-------------+--------------+ ++--------------------------------------------------------------+ |server_keytab|Use: heimdal_gssapi|Type: string*|Default: unset| -+-------------+-------------------+-------------+--------------+ ++--------------------------------------------------------------+ If set, then Heimdal will not use the system default keytab (typically /etc/ krb5.keytab) but instead the pathname given in this option. The value should be a pathname, with no "file:" prefix. -+--------------+-------------------+-------------+-------------+ ++--------------------------------------------------------------+ |server_service|Use: heimdal_gssapi|Type: string*|Default: smtp| -+--------------+-------------------+-------------+-------------+ ++--------------------------------------------------------------+ This option specifies the service identifier used, in conjunction with server_hostname, for building the identifer for finding credentials from the @@ -24213,9 +24593,9 @@ The spa authenticator has just one server option: -+---------------+--------+-------------+--------------+ ++-----------------------------------------------------+ |server_password|Use: spa|Type: string*|Default: unset| -+---------------+--------+-------------+--------------+ ++-----------------------------------------------------+ This option is expanded, and the result must be the cleartext password for the authenticating user, whose name is at this point in $auth1. For compatibility @@ -24239,21 +24619,21 @@ The spa authenticator has the following client options: -+-------------+--------+-------------+--------------+ ++---------------------------------------------------+ |client_domain|Use: spa|Type: string*|Default: unset| -+-------------+--------+-------------+--------------+ ++---------------------------------------------------+ This option specifies an optional domain for the authentication. -+---------------+--------+-------------+--------------+ ++-----------------------------------------------------+ |client_password|Use: spa|Type: string*|Default: unset| -+---------------+--------+-------------+--------------+ ++-----------------------------------------------------+ This option specifies the user's password, and must be set. -+---------------+--------+-------------+--------------+ ++-----------------------------------------------------+ |client_username|Use: spa|Type: string*|Default: unset| -+---------------+--------+-------------+--------------+ ++-----------------------------------------------------+ This option specifies the user name, and must be set. Here is an example of a configuration of this authenticator for use with the mail servers at msn.com: @@ -24268,7 +24648,69 @@ =============================================================================== -41. ENCRYPTED SMTP CONNECTIONS USING TLS/SSL +41. THE TLS AUTHENTICATOR + +The tls authenticator provides server support for authentication based on +client certificates. + +It is not an SMTP authentication mechanism and is not advertised by the server +as part of the SMTP EHLO response. It is an Exim authenticator in the sense +that it affects the protocol element of the log line, can be tested for by the +authenticated ACL condition, and can set the $authenticated_id variable. + +The client must present a verifiable certificate, for which it must have been +requested via the tls_verify_hosts or tls_try_verify_hosts main options (see 42 +). + +If an authenticator of this type is configured it is run before any SMTP-level +communication is done, and can authenticate the connection. If it does, SMTP +suthentication is not offered. + +A maximum of one authenticator of this type may be present. + +The tls authenticator has three server options: + ++---------------------------------------------------+ +|server_param1|Use: tls|Type: string*|Default: unset| ++---------------------------------------------------+ + +This option is expanded after the TLS negotiation and the result is placed in +$auth1. If the expansion is forced to fail, authentication fails. Any other +expansion failure causes a temporary error code to be returned. + ++---------------------------------------------------+ +|server_param2|Use: tls|Type: string*|Default: unset| ++---------------------------------------------------+ + ++---------------------------------------------------+ +|server_param3|Use: tls|Type: string*|Default: unset| ++---------------------------------------------------+ + +As above, for $auth2 and $auth3. + +server_param1 may also be spelled server_param. + +Example: + +tls: + driver = tls + server_param1 = ${certextract {subj_altname,mail,>:} \ + {$tls_in_peercert}} + server_condition = ${if forany {$auth1} \ + {!= {0} \ + {${lookup ldap{ldap:///\ + mailname=${quote_ldap_dn:${lc:$item}},\ + ou=users,LDAP_DC?mailid} {$value}{0} \ + } } } } + server_set_id = ${if = {1}{${listcount:$auth1}} {$auth1}{}} + +Note that because authentication is traditionally an SMTP operation, the +authenticated ACL condition cannot be used in a connect- or helo-ACL. + + + +=============================================================================== +42. ENCRYPTED SMTP CONNECTIONS USING TLS/SSL Support for TLS (Transport Layer Security), formerly known as SSL (Secure Sockets Layer), is implemented by making use of the OpenSSL library or the @@ -24296,7 +24738,7 @@ to get TLS to work. -41.1 Support for the legacy "ssmtp" (aka "smtps") protocol +42.1 Support for the legacy "ssmtp" (aka "smtps") protocol ---------------------------------------------------------- Early implementations of encrypted SMTP used a different TCP port from normal @@ -24322,7 +24764,7 @@ tls_on_connect_ports; it forces the legacy behaviour for all ports. -41.2 OpenSSL vs GnuTLS +42.2 OpenSSL vs GnuTLS ---------------------- The first TLS support in Exim was implemented using OpenSSL. Support for GnuTLS @@ -24340,8 +24782,9 @@ There are some differences in usage when using GnuTLS instead of OpenSSL: - * The tls_verify_certificates option must contain the name of a file, not the - name of a directory (for OpenSSL it can be either). + * The tls_verify_certificates option cannot be the path of a directory for + GnuTLS versions before 3.3.6 (for later versions, or OpenSSL, it can be + either). * The default value for tls_dhparam differs for historical reasons. @@ -24359,7 +24802,7 @@ transport option). * The tls_require_ciphers options operate differently, as described in the - sections 41.4 and 41.5. + sections 42.4 and 42.5. * The tls_dh_min_bits SMTP transport option is only honoured by GnuTLS. When using OpenSSL, this option is ignored. (If an API is found to let OpenSSL @@ -24372,7 +24815,7 @@ implementation, then patches are welcome. -41.3 GnuTLS parameter computation +42.3 GnuTLS parameter computation --------------------------------- This section only applies if tls_dhparam is set to "historic" or to an explicit @@ -24455,7 +24898,7 @@ the generated prime, so it might still be too large. -41.4 Requiring specific ciphers in OpenSSL +42.4 Requiring specific ciphers in OpenSSL ------------------------------------------ There is a function in the OpenSSL library that can be passed a list of cipher @@ -24512,14 +24955,15 @@ {HIGH:!MD5:!SHA1}} -41.5 Requiring specific ciphers or other parameters in GnuTLS +42.5 Requiring specific ciphers or other parameters in GnuTLS ------------------------------------------------------------- The GnuTLS library allows the caller to provide a "priority string", documented as part of the gnutls_priority_init function. This is very similar to the ciphersuite specification in OpenSSL. -The tls_require_ciphers option is treated as the GnuTLS priority string. +The tls_require_ciphers option is treated as the GnuTLS priority string and +controls both protocols and ciphers. The tls_require_ciphers option is available both as an global option, controlling how Exim behaves as a server, and also as an option of the smtp @@ -24534,6 +24978,11 @@ newer than the version installed on your system. If you are using GnuTLS 3, then the example code on that site can be used to test a given string. +For example: + +# Disable older versions of protocols +tls_require_ciphers = NORMAL:%LATEST_RECORD_VERSION:-VERS-SSL3.0 + Prior to Exim 4.80, an older API of GnuTLS was used, and Exim supported three additional options, "gnutls_require_kx", "gnutls_require_mac" and " gnutls_require_protocols". tls_require_ciphers was an Exim list. @@ -24550,7 +24999,7 @@ {SECURE128}} -41.6 Configuring an Exim server to use TLS +42.6 Configuring an Exim server to use TLS ------------------------------------------ When Exim has been built with TLS support, it advertises the availability of @@ -24583,17 +25032,17 @@ These options are, in fact, expanded strings, so you can make them depend on the identity of the client that is connected if you wish. The first file contains the server's X509 certificate, and the second contains the private key -that goes with it. These files need to be readable by the Exim user, and must -always be given as full path names. They can be the same file if both the -certificate and the key are contained within it. If tls_privatekey is not set, -or if its expansion is forced to fail or results in an empty string, this is -assumed to be the case. The certificate file may also contain intermediate -certificates that need to be sent to the client to enable it to authenticate -the server's certificate. +that goes with it. These files need to be PEM format and readable by the Exim +user, and must always be given as full path names. The key must not be +password-protected. They can be the same file if both the certificate and the +key are contained within it. If tls_privatekey is not set, or if its expansion +is forced to fail or results in an empty string, this is assumed to be the +case. The certificate file may also contain intermediate certificates that need +to be sent to the client to enable it to authenticate the server's certificate. If you do not understand about certificates and keys, please try to find a source of this background information, which is not Exim-specific. (There are a -few comments below in section 41.12.) +few comments below in section 42.12.) Note: These options do not apply when Exim is operating as a client - they apply only in the case of a server. If you need to use a certificate in an Exim @@ -24646,7 +25095,7 @@ depending on the tls_cipher log selector). -41.7 Requesting and verifying client certificates +42.7 Requesting and verifying client certificates ------------------------------------------------- If you want an Exim server to request a certificate when negotiating a TLS @@ -24655,8 +25104,12 @@ all TLS connections. For any host that matches one of these options, Exim requests a certificate as part of the setup of the TLS session. The contents of the certificate are verified by comparing it with a list of expected -certificates. These must be available in a file or, for OpenSSL only (not -GnuTLS), a directory, identified by tls_verify_certificates. +certificates. + +These may be the system default set (depending on library version), + +an explicit file or, depending on library version, a directory, identified by +tls_verify_certificates. A file can contain multiple certificates, concatenated end to end. If a directory is used (OpenSSL only), each certificate must be in a separate file, @@ -24690,7 +25143,7 @@ is empty. -41.8 Revoked certificates +42.8 Revoked certificates ------------------------- Certificate issuing authorities issue Certificate Revocation Lists (CRLs) when @@ -24699,7 +25152,7 @@ identically named option for the smtp transport. In each case, the value of the option is expanded and must then be the name of a file that contains a CRL in PEM format. The downside is that clients have to periodically re-download a -potentially huge file from every certificate authority the know of. +potentially huge file from every certificate authority they know of. The way with most moving parts at query time is Online Certificate Status Protocol (OCSP), where the client verifies the certificate against an OCSP @@ -24759,7 +25212,7 @@ noted this as invalid overall, but the re-fetch script did not. -41.9 Configuring an Exim client to use TLS +42.9 Configuring an Exim client to use TLS ------------------------------------------ The tls_cipher and tls_peerdn log selectors apply to outgoing SMTP deliveries @@ -24796,9 +25249,13 @@ If the server is Exim, it will request a certificate only if tls_verify_hosts or tls_try_verify_hosts matches the client. -If the tls_verify_certificates option is set on the smtp transport, it must -name a file or, for OpenSSL only (not GnuTLS), a directory, that contains a -collection of expected server certificates. The client verifies the server's +If the tls_verify_certificates option is set on the smtp transport, it +specifies a collection of expected server certificates. + +These may be the system default set (depending on library version), + +a file or, depnding on liibrary version, a directory, must name a file or, for +OpenSSL only (not GnuTLS), a directory. The client verifies the server's certificate against this collection, taking into account any revoked certificates that are in the list defined by tls_crl. Failure to verify fails the TLS connection unless either of the tls_verify_hosts or @@ -24840,7 +25297,7 @@ set to the relevant values for the outgoing connection. -41.10 Use of TLS Server Name Indication +42.10 Use of TLS Server Name Indication --------------------------------------- With TLS1.0 or above, there is an extension mechanism by which extra @@ -24886,7 +25343,7 @@ * tls_verify_certificates - * tls_verify_certificates + * tls_ocsp_file Great care should be taken to deal with matters of case, various injection attacks in the string ("../" or SQL), and ensuring that a valid filename can @@ -24906,7 +25363,7 @@ built, then you have SNI support). -41.11 Multiple messages on the same encrypted TCP/IP connection +42.11 Multiple messages on the same encrypted TCP/IP connection --------------------------------------------------------------- Exim sends multiple messages down the same TCP/IP connection by starting up an @@ -24939,7 +25396,7 @@ new processes if TLS has been used. -41.12 Certificates and all that +42.12 Certificates and all that ------------------------------- In order to understand fully how TLS works, you need to know about @@ -24958,7 +25415,7 @@ http://www.rtfm.com/openssl-examples/ -41.13 Certificate chains +42.13 Certificate chains ------------------------ The file named by tls_certificate may contain more than one certificate. This @@ -24980,7 +25437,7 @@ in such a case can be frustratingly vague. -41.14 Self-signed certificates +42.14 Self-signed certificates ------------------------------ You can create a self-signed certificate using the req command provided with @@ -25024,7 +25481,7 @@ =============================================================================== -42. ACCESS CONTROL LISTS +43. ACCESS CONTROL LISTS Access Control Lists (ACLs) are defined in a separate section of the run time configuration file, headed by "begin acl". Each ACL definition starts with a @@ -25047,16 +25504,16 @@ a realistic ACL for checking RCPT commands. This is discussed in chapter 7. -42.1 Testing ACLs +43.1 Testing ACLs ----------------- The -bh command line option provides a way of testing your ACL configuration locally by running a fake SMTP session with which you interact. The host relay-test.mail-abuse.org provides a service for checking your relaying -configuration (see section 42.53 for more details). +configuration (see section 43.53 for more details). -42.2 Specifying when ACLs are used +43.2 Specifying when ACLs are used ---------------------------------- In order to cause an ACL to be used, you have to name it in one of the relevant @@ -25069,6 +25526,7 @@ acl_smtp_connect ACL for start of SMTP connection acl_smtp_data ACL after DATA is complete acl_smtp_data_prdr ACL for each recipient, after DATA is complete + acl_smtp_dkim ACL for each DKIM signer acl_smtp_etrn ACL for ETRN acl_smtp_expn ACL for EXPN acl_smtp_helo ACL for HELO or EHLO @@ -25095,7 +25553,7 @@ possible at RCPT time. -42.3 The non-SMTP ACLs +43.3 The non-SMTP ACLs ---------------------- The non-SMTP ACLs apply to all non-interactive incoming messages, that is, they @@ -25122,14 +25580,14 @@ run, it is too late. The acl_not_smtp_mime ACL is available only when Exim is compiled with the -content-scanning extension. For details, see chapter 43. +content-scanning extension. For details, see chapter 44. The acl_not_smtp ACL is run just before the local_scan() function. Any kind of rejection is treated as permanent, because there is no way of sending a temporary error for these kinds of message. -42.4 The SMTP connect ACL +43.4 The SMTP connect ACL ------------------------- The ACL test specified by acl_smtp_connect happens at the start of an SMTP @@ -25140,7 +25598,7 @@ smtp_banner option. -42.5 The EHLO/HELO ACL +43.5 The EHLO/HELO ACL ---------------------- The ACL test specified by acl_smtp_helo happens when the client issues an EHLO @@ -25156,7 +25614,7 @@ options that are listed on the second and subsequent lines of an EHLO response. -42.6 The DATA ACLs +43.6 The DATA ACLs ------------------ Two ACLs are associated with the DATA command, because it is two-stage command, @@ -25185,7 +25643,7 @@ and the acl_smtp_mime ACLs. -42.7 The SMTP DKIM ACL +43.7 The SMTP DKIM ACL ---------------------- The acl_smtp_dkim ACL is available only when Exim is compiled with DKIM support @@ -25197,19 +25655,19 @@ This ACL is evaluated before acl_smtp_mime and acl_smtp_data. -For details on the operation of DKIM, see chapter 56. +For details on the operation of DKIM, see chapter 57. -42.8 The SMTP MIME ACL +43.8 The SMTP MIME ACL ---------------------- The acl_smtp_mime option is available only when Exim is compiled with the -content-scanning extension. For details, see chapter 43. +content-scanning extension. For details, see chapter 44. This ACL is evaluated after acl_smtp_dkim but before acl_smtp_data. -42.9 The SMTP PRDR ACL +43.9 The SMTP PRDR ACL ---------------------- The acl_smtp_data_prdr ACL is available only when Exim is compiled with PRDR @@ -25218,10 +25676,11 @@ one recipient has been accepted. The ACL test specfied by acl_smtp_data_prdr happens after a message has been -recieved, and is executed for each recipient of the message. The test may -accept or deny for inividual recipients. The acl_smtp_data will still be called -after this ACL and can reject the message overall, even if this ACL has -accepted it for some or all recipients. +recieved, and is executed once for each recipient of the message with +$local_part and $domain valid. The test may accept, defer or deny for inividual +recipients. The acl_smtp_data will still be called after this ACL and can +reject the message overall, even if this ACL has accepted it for some or all +recipients. PRDR may be used to support per-user content filtering. Without it one must defer any recipient after the first that has a different content-filter @@ -25239,13 +25698,14 @@ client. -42.10 The QUIT ACL +43.10 The QUIT ACL ------------------ The ACL for the SMTP QUIT command is anomalous, in that the outcome of the ACL does not affect the response code to QUIT, which is always 221. Thus, the ACL -does not in fact control any access. For this reason, the only verbs that are -permitted are accept and warn. +does not in fact control any access. + +For this reason, it may only accept or warn as its final result. This ACL can be used for tasks such as custom logging at the end of an SMTP session. For example, you can use ACL variables in other ACLs to count @@ -25266,7 +25726,7 @@ connection is closed. In these special cases, the QUIT ACL does not run. -42.11 The not-QUIT ACL +43.11 The not-QUIT ACL ---------------------- The not-QUIT ACL, specified by acl_smtp_notquit, is run in most cases when an @@ -25301,7 +25761,7 @@ verb in another ACL, it is the message from the other ACL that is used. -42.12 Finding an ACL to use +43.12 Finding an ACL to use --------------------------- The value of an acl_smtp_xxx option is expanded before use, so you can use @@ -25352,11 +25812,11 @@ file. -42.13 ACL return codes +43.13 ACL return codes ---------------------- Except for the QUIT ACL, which does not affect the SMTP return code (see -section 42.10 above), the result of running an ACL is either "accept" or +section 43.10 above), the result of running an ACL is either "accept" or "deny", or, if some test cannot be completed (for example, if a database is down), "defer". These results cause 2xx, 5xx, and 4xx return codes, respectively, to be used in the SMTP dialogue. A fourth return, "error", occurs @@ -25383,7 +25843,7 @@ recipients; it may create new recipients. -42.14 Unset ACL options +43.14 Unset ACL options ----------------------- The default actions when any of the acl_xxx options are unset are not all the @@ -25405,7 +25865,7 @@ connection. For an example, see the ACL in the default configuration file. -42.15 Data for message ACLs +43.15 Data for message ACLs --------------------------- When a MAIL or RCPT ACL, or either of the DATA ACLs, is running, the variables @@ -25432,7 +25892,7 @@ contains the total number of accepted recipients. -42.16 Data for non-message ACLs +43.16 Data for non-message ACLs ------------------------------- When an ACL is being run for AUTH, EHLO, ETRN, EXPN, HELO, STARTTLS, or VRFY, @@ -25455,7 +25915,7 @@ option to do this.) -42.17 Format of an ACL +43.17 Format of an ACL ---------------------- An individual ACL consists of a number of statements. Each statement starts @@ -25478,7 +25938,7 @@ test a sender address in the ACL that is run for a VRFY command. -42.18 ACL verbs +43.18 ACL verbs --------------- The ACL verbs are as follows: @@ -25571,7 +26031,7 @@ passes control to subsequent statements only if the message's sender can be verified. Otherwise, it rejects the command. Note the positioning of the message modifier, before the verify condition. The reason for this is - discussed in section 42.20. + discussed in section 43.20. * warn: If all the conditions are true, a line specified by the log_message modifier is written to Exim's main log. Control always passes to the next @@ -25583,7 +26043,7 @@ If log_message is not present, a warn verb just checks its conditions and obeys any "immediate" modifiers (such as control, set, logwrite, add_header , and remove_header) that appear before the first failing condition. There - is more about adding header lines in section 42.24. + is more about adding header lines in section 43.24. If any condition on a warn statement cannot be completed (that is, there is some sort of defer), the log line specified by log_message is not written. @@ -25609,7 +26069,7 @@ mechanism. It is conventional to align the conditions vertically. -42.19 ACL variables +43.19 ACL variables ------------------- There are some special variables that can be set during ACL processing. They @@ -25651,7 +26111,7 @@ their names are compatible, so there is no problem with upgrading. -42.20 Condition and modifier processing +43.20 Condition and modifier processing --------------------------------------- An exclamation mark preceding a condition negates its result. For example: @@ -25724,7 +26184,7 @@ which time Exim has set up the message. -42.21 ACL modifiers +43.21 ACL modifiers ------------------- The ACL modifiers are as follows: @@ -25733,7 +26193,7 @@ This modifier specifies one or more header lines that are to be added to an incoming message, assuming, of course, that the message is ultimately - accepted. For details, see section 42.24. + accepted. For details, see section 43.24. continue = @@ -25759,7 +26219,7 @@ individual recipients, even if the control modifier appears in a RCPT ACL. As there are now quite a few controls that can be applied, they are - described separately in section 42.22. The control modifier can be used in + described separately in section 43.22. The control modifier can be used in several different ways. For example: * It can be at the end of an accept statement: @@ -25973,6 +26433,9 @@ processed anyway. If the message contains newlines, this gives rise to a multi-line SMTP response. + For ACLs that are called by an acl = ACL condition, the message is stored + in $acl_verify_message, from which the calling ACL may use it. + If message is used on a statement that verifies an address, the message specified overrides any message that is generated by the verification process. However, the original message is available in the variable @@ -25992,11 +26455,11 @@ This modifier specifies one or more header names in a colon-separated list that are to be removed from an incoming message, assuming, of course, that - the message is ultimately accepted. For details, see section 42.25. + the message is ultimately accepted. For details, see section 43.25. set = - This modifier puts a value into one of the ACL variables (see section 42.19 + This modifier puts a value into one of the ACL variables (see section 43.19 ). udpsend = @@ -26013,7 +26476,7 @@ $tod_zulu $sender_host_address -42.22 Use of the control modifier +43.22 Use of the control modifier --------------------------------- The control modifier supports the following settings: @@ -26070,10 +26533,24 @@ control = cutthrough_delivery This option requests delivery be attempted while the item is being - received. It is usable in the RCPT ACL and valid only for single-recipient - mails forwarded from one SMTP connection to another. If a recipient-verify - callout connection is requested in the same ACL it is held open and used - for the data, otherwise one is made after the ACL completes. + received. + + The option is usable in the RCPT ACL. If enabled for a message recieved via + smtp and routed to an smtp transport, + + and only one transport, interface, destination host and port combination is + used for all recipients of the message, + + then the delivery connection is made while the receiving connection is open + and data is copied from one to the other. + + An attempt to set this option for any recipient but the first for a mail + will be quietly ignored. If a recipient-verify callout connection is + subsequently requested in the same ACL it is held open and used for + + any subsequent receipients and the data, + + otherwise one is made after the initial RCPT ACL completes. Note that routers are used in verify mode, and cannot depend on content of received headers. Note also that headers cannot be modified by any of the @@ -26084,12 +26561,14 @@ signing of outgoing messages is done, because it sends data to the ultimate destination before the entire message has been received from the source. + It is not supported for messages recieved with the SMTP PRDR option in use. + Should the ultimate destination system positively accept or reject the mail, a corresponding indication is given to the source system and nothing is queued. If there is a temporary error the item is queued for later delivery in the usual fashion. If the item is successfully delivered in - cutthrough mode the log line is tagged with ">>" rather than "=>" and - appears before the acceptance "<=" line. + cutthrough mode the delivery log lines are tagged with ">>" rather than "=> + " and appear before the acceptance "<=" line. Delivery in this mode avoids the generation of a bounce mail to a (possibly faked) sender when the destination system is doing content-scan based @@ -26112,7 +26591,7 @@ control = dkim_disable_verify This control turns off DKIM verification processing entirely. For details - on the operation and configuration of DKIM, see chapter 56. + on the operation and configuration of DKIM, see chapter 57. control = dscp/ @@ -26265,8 +26744,8 @@ not present. This control is not permitted in the acl_smtp_data ACL, because that is too late (the message has already been created). - Chapter 46 describes the processing that Exim applies to messages. Section - 46.1 covers the processing that happens in submission mode; the available + Chapter 47 describes the processing that Exim applies to messages. Section + 47.1 covers the processing that happens in submission mode; the available options for this control are described there. The control applies only to the current message, not to any subsequent ones that may be received in the same SMTP connection. @@ -26294,7 +26773,7 @@ that are being submitted at the same time using -bs or -bS. -42.23 Summary of message fixup control +43.23 Summary of message fixup control -------------------------------------- All four possibilities for message fixups can be specified: @@ -26309,7 +26788,7 @@ * Remotely submitted, fixups applied: use "control = submission". -42.24 Adding header lines in ACLs +43.24 Adding header lines in ACLs --------------------------------- The add_header modifier can be used to add one or more extra header lines to an @@ -26352,7 +26831,7 @@ run. Similarly, header lines that are added by the DATA or MIME ACLs are not visible in those ACLs. Because of this restriction, you cannot use header lines as a way of passing data between (for example) the MAIL and RCPT ACLs. If you -want to do this, you can use ACL variables, as described in section 42.19. +want to do this, you can use ACL variables, as described in section 43.19. The list of headers yet to be added is given by the $headers_added variable. @@ -26402,7 +26881,7 @@ in a router or transport. -42.25 Removing header lines in ACLs +43.25 Removing header lines in ACLs ----------------------------------- The remove_header modifier can be used to remove one or more header lines from @@ -26449,7 +26928,7 @@ removed by the DATA or MIME ACLs are still visible in those ACLs. Because of this restriction, you cannot use header lines as a way of controlling data passed between (for example) the MAIL and RCPT ACLs. If you want to do this, -you should instead use ACL variables, as described in section 42.19. +you should instead use ACL variables, as described in section 43.19. The remove_header modifier acts immediately as it is encountered during the processing of an ACL. Notice the difference between these two cases: @@ -26471,13 +26950,13 @@ system filter or in a router or transport. -42.26 ACL conditions +43.26 ACL conditions -------------------- Some of the conditions listed in this section are available only when Exim is compiled with the content-scanning extension. They are included here briefly for completeness. More detailed descriptions can be found in the discussion on -content scanning in chapter 43. +content scanning in chapter 44. Not all conditions are relevant in all circumstances. For example, testing senders and recipients does not make sense in an ACL that is being run as the @@ -26501,7 +26980,8 @@ can be appended; they appear within the called ACL in $acl_arg1 to $acl_arg9, and $acl_narg is set to the count of values. Previous values of these variables are restored after the call returns. The name and values - are expanded separately. + are expanded separately. Note that spaces in complex expansions which are + used as arguments will act as argument separators. If the nested acl returns "drop" and the outer condition denies access, the connection is dropped. If it returns "discard", the verb must be accept or @@ -26539,12 +27019,12 @@ acl_smtp_mime. It causes the current MIME part to be decoded into a file. If all goes well, the condition is true. It is false only if there are problems such as a syntax error or a memory shortage. For more details, see - chapter 43. + chapter 44. demime = This condition is available only when Exim is compiled with the - content-scanning extension. Its use is described in section 43.6. + content-scanning extension. Its use is described in section 44.6. dnslists = @@ -26552,7 +27032,7 @@ as "RBL lists", after the original Realtime Blackhole List, but note that the use of the lists at mail-abuse.org now carries a charge. There are too many different variants of this condition to describe briefly here. See - sections 42.27-42.37 for details. + sections 43.27-43.37 for details. domains = @@ -26625,19 +27105,19 @@ This condition is available only when Exim is compiled with the content-scanning extension. It causes the incoming message to be scanned - for viruses. For details, see chapter 43. + for viruses. For details, see chapter 44. mime_regex = This condition is available only when Exim is compiled with the content-scanning extension, and it is allowed only in the ACL defined by acl_smtp_mime. It causes the current MIME part to be scanned for a match - with any of the regular expressions. For details, see chapter 43. + with any of the regular expressions. For details, see chapter 44. ratelimit = This condition can be used to limit the rate at which a user or host - submits messages. Details are given in section 42.38. + submits messages. Details are given in section 43.38. recipients =
@@ -26649,7 +27129,7 @@ This condition is available only when Exim is compiled with the content-scanning extension, and is available only in the DATA, MIME, and non-SMTP ACLs. It causes the incoming message to be scanned for a match - with any of the regular expressions. For details, see chapter 43. + with any of the regular expressions. For details, see chapter 44. sender_domains = @@ -26678,19 +27158,19 @@ This condition is available only when Exim is compiled with the content-scanning extension. It causes the incoming message to be scanned by - SpamAssassin. For details, see chapter 43. + SpamAssassin. For details, see chapter 44. verify = certificate This condition is true in an SMTP session if the session is encrypted, and a certificate was received from the client, and the certificate was verified. The server requests a certificate only if the client matches - tls_verify_hosts or tls_try_verify_hosts (see chapter 41). + tls_verify_hosts or tls_try_verify_hosts (see chapter 42). verify = csa This condition checks whether the sending host (the client) is authorized - to send email. Details of how this works are given in section 42.50. + to send email. Details of how this works are given in section 43.50. verify = header_names_ascii @@ -26718,7 +27198,7 @@ command. Details of address verification and the options are given later, starting - at section 42.44 (callouts are described in section 42.45). You can combine + at section 43.44 (callouts are described in section 43.45). You can combine this condition with the senders condition to restrict it to bounce messages only: @@ -26772,14 +27252,14 @@ This condition is relevant only after a RCPT command. It verifies the current recipient. Details of address verification are given later, - starting at section 42.44. After a recipient has been verified, the value + starting at section 43.44. After a recipient has been verified, the value of $address_data is the last value that was set while routing the address. This applies even if the verification fails. When an address that is being verified is redirected to a single address, verification continues with the new address, and in that case, the subsequent value of $address_data is the value for the child address. -verify = reverse_host_lookup +verify = reverse_host_lookup/ This condition ensures that a verified host name has been looked up from the IP address of the client host. (This may have happened already if the @@ -26788,6 +27268,9 @@ reverse DNS lookup, or one of its aliases, does, when it is itself looked up in the DNS, yield the original IP address. + There is one possible option, "defer_ok". If this is present and a DNS + operation returns a temporary error, the verify condition succeeds. + If this condition is used for a locally generated message (that is, when there is no client host involved), it always succeeds. @@ -26805,7 +27288,7 @@ you want to preserve the value for longer, you can save it in an ACL variable. - Details of verification are given later, starting at section 42.44. Exim + Details of verification are given later, starting at section 43.44. Exim caches the result of sender verification, to avoid doing it more than once per message. @@ -26815,7 +27298,7 @@ verified as a sender. -42.27 Using DNS lists +43.27 Using DNS lists --------------------- In its simplest form, the dnslists condition tests whether the calling host is @@ -26872,7 +27355,7 @@ connections (but your local name server cache should be active). -42.28 Specifying the IP address for a DNS list lookup +43.28 Specifying the IP address for a DNS list lookup ----------------------------------------------------- By default, the IP address that is used in a DNS list lookup is the IP address @@ -26884,10 +27367,10 @@ This feature is not very helpful with explicit IP addresses; it is intended for use with IP addresses that are looked up, for example, the IP addresses of the MX hosts or nameservers of an email sender address. For an example, see section -42.30 below. +43.30 below. -42.29 DNS lists keyed on domain names +43.29 DNS lists keyed on domain names ------------------------------------- There are some lists that are keyed on domain names rather than inverted IP @@ -26916,7 +27399,7 @@ name. The whole condition is true if either of the DNS lookups succeeds. -42.30 Multiple explicit keys for a DNS list +43.30 Multiple explicit keys for a DNS list ------------------------------------------- The syntax described above for looking up explicitly-defined values (either @@ -26944,7 +27427,7 @@ a.domain.black.list.tld Once a DNS record has been found (that matches a specific IP return address, if -specified - see section 42.33), no further lookups are done. If there is a +specified - see section 43.33), no further lookups are done. If there is a temporary DNS error, the rest of the sublist of domains or IP addresses is tried. A temporary error for the whole dnslists item occurs only if no other DNS lookup in this sublist succeeds. In other words, a successful lookup for @@ -26979,10 +27462,10 @@ domain's mail servers are on the Spamhaus black list. The key that was used for a successful DNS list lookup is put into the variable -$dnslist_matched (see section 42.32). +$dnslist_matched (see section 43.32). -42.31 Data returned by DNS lists +43.31 Data returned by DNS lists -------------------------------- DNS lists are constructed using address records in the DNS. The original RBL @@ -26998,12 +27481,12 @@ 127.1.0.6 RSS and DUL 127.1.0.7 RSS and DUL and RBL -Section 42.33 below describes how you can distinguish between different values. -Some DNS lists may return more than one address record; see section 42.35 for +Section 43.33 below describes how you can distinguish between different values. +Some DNS lists may return more than one address record; see section 43.35 for details of how they are checked. -42.32 Variables set from DNS lists +43.32 Variables set from DNS lists ---------------------------------- When an entry is found in a DNS list, the variable $dnslist_domain contains the @@ -27017,7 +27500,7 @@ the key is also available in another variable (in this case, $sender_host_address). In more complicated cases, however, this is not true. -For example, using a data lookup (as described in section 42.30) might generate +For example, using a data lookup (as described in section 43.30) might generate a dnslists lookup like this: deny dnslists = spamhaus.example/<|192.168.1.2|192.168.6.7|... @@ -27029,7 +27512,7 @@ addresses are included in $dnslist_value, separated by commas and spaces. The variable $dnslist_text contains the contents of any associated TXT record. For lists such as RBL+ the TXT record for a merged entry is often not very -meaningful. See section 42.36 for a way of obtaining more information. +meaningful. See section 43.36 for a way of obtaining more information. You can use the DNS list variables in message or log_message modifiers - although these appear before the condition in the ACL, they are not expanded @@ -27041,7 +27524,7 @@ dnslists = rbl-plus.mail-abuse.example -42.33 Additional matching conditions for DNS lists +43.33 Additional matching conditions for DNS lists -------------------------------------------------- You can add an equals sign and an IP address after a dnslists domain name in @@ -27052,7 +27535,7 @@ rejects only those hosts that yield 127.0.0.2. Without this additional data, any address record is considered to be a match. For the moment, we assume that -the DNS lookup returns just one record. Section 42.35 describes how multiple +the DNS lookup returns just one record. Section 43.35 describes how multiple records are handled. More than one IP address may be given for checking, using a comma as a @@ -27086,7 +27569,7 @@ odd number. -42.34 Negated DNS matching conditions +43.34 Negated DNS matching conditions ------------------------------------- You can supply a negative list of IP addresses as part of a dnslists condition. @@ -27132,7 +27615,7 @@ which is less clear, and harder to maintain. -42.35 Handling multiple DNS records from a DNS list +43.35 Handling multiple DNS records from a DNS list --------------------------------------------------- A DNS lookup for a dnslists condition may return more than one DNS record, @@ -27194,7 +27677,7 @@ between "=" and "==" and between "&" and "=&". -42.36 Detailed information from merged DNS lists +43.36 Detailed information from merged DNS lists ------------------------------------------------ When the facility for restricting the matching IP values in a DNS list is used, @@ -27243,7 +27726,7 @@ done. Only if there is a match is one of the more specific lists consulted. -42.37 DNS lists and IPv6 +43.37 DNS lists and IPv6 ------------------------ If Exim is asked to do a dnslist lookup for an IPv6 address, it inverts it @@ -27275,7 +27758,7 @@ dnslists = <; dnsbl.example.com/<|$acl_m_addrslist -42.38 Rate limiting incoming messages +43.38 Rate limiting incoming messages ------------------------------------- The ratelimit ACL condition can be used to measure and control the rate at @@ -27339,7 +27822,7 @@ lookup key is not affected by changes to the update mode and the count= option. -42.39 Ratelimit options for what is being measured +43.39 Ratelimit options for what is being measured -------------------------------------------------- The per_conn option limits the client's connection rate. It is not normally @@ -27362,8 +27845,9 @@ accepted. It can be used in the acl_smtp_rcpt, acl_smtp_predata, acl_smtp_mime, acl_smtp_data, or acl_smtp_rcpt ACLs. In acl_smtp_rcpt the rate is updated one recipient at a time; in the other ACLs the rate is updated with the total -recipient count in one go. Note that in either case the rate limiting engine -will see a message with many recipients as a large high-speed burst. +(accepted) recipient count in one go. Note that in either case the rate +limiting engine will see a message with many recipients as a large high-speed +burst. The per_addr option is like the per_rcpt option, except it counts the number of different recipients that the client has sent messages to in the last time @@ -27382,10 +27866,10 @@ rate by one (except for the per_rcpt option in ACLs other than acl_smtp_rcpt). The count does not have to be an integer. -The unique= option is described in section 42.42 below. +The unique= option is described in section 43.42 below. -42.40 Ratelimit update modes +43.40 Ratelimit update modes ---------------------------- You can specify one of three options with the ratelimit condition to control @@ -27424,7 +27908,7 @@ specify the readonly option explicitly. -42.41 Ratelimit options for handling fast clients +43.41 Ratelimit options for handling fast clients ------------------------------------------------- If a client's average rate is greater than the maximum, the rate limiting @@ -27454,7 +27938,7 @@ ln(peakrate/maxrate) -42.42 Limiting the rate of different events +43.42 Limiting the rate of different events ------------------------------------------- The ratelimit unique= option controls a mechanism for counting the rate of @@ -27491,7 +27975,7 @@ intended. -42.43 Using rate limiting +43.43 Using rate limiting ------------------------- Exim's other ACL facilities are used to define what counter-measures are taken @@ -27535,11 +28019,11 @@ ratelimit data). -42.44 Address verification +43.44 Address verification -------------------------- -Several of the verify conditions described in section 42.26 cause addresses to -be verified. Section 42.48 discusses the reporting of sender verification +Several of the verify conditions described in section 43.26 cause addresses to +be verified. Section 43.48 discusses the reporting of sender verification failures. The verification conditions can be followed by options that modify the verification process. The options are separated from the keyword and from each other by slashes, and some of them contain parameters. For example: @@ -27563,13 +28047,13 @@ the condition is forced to be true instead. Note that this is a main verification option as well as a suboption for callouts. - * The no_details option is covered in section 42.48, which discusses the + * The no_details option is covered in section 43.48, which discusses the reporting of sender address verification failures. * The success_on_redirect option causes verification always to succeed immediately after a successful redirection. By default, if a redirection generates just one address, that address is also verified. See further - discussion in section 42.49. + discussion in section 43.49. After an address verification failure, $acl_verify_message contains the error message that is associated with the failure. It can be preserved by coding like @@ -27601,7 +28085,7 @@ rejections of MAIL and rejections of RCPT in callouts. -42.45 Callout verification +43.45 Callout verification -------------------------- For non-local addresses, routing verifies the domain, but is unable to do any @@ -27619,7 +28103,7 @@ described below. This facility should be used with care, because it can add a lot of resource usage to the cost of verifying an address. However, Exim does cache the results of callouts, which helps to reduce the cost. Details of -caching are in section 42.47. +caching are in section 43.47. Recipient callouts are usually used only between hosts that are controlled by the same administration. For example, a corporate gateway host could use @@ -27678,7 +28162,7 @@ disabled by using a control modifier to set no_callout_flush. -42.46 Additional parameters for callouts +43.46 Additional parameters for callouts ---------------------------------------- The callout option can be followed by an equals sign and a number of optional @@ -27843,7 +28327,7 @@ callouts are performed than when an empty sender or postmaster is used. -42.47 Callout caching +43.47 Callout caching --------------------- Exim caches the results of callouts in order to reduce the amount of resources @@ -27884,10 +28368,10 @@ behaviour will be the same. -42.48 Sender address verification reporting +43.48 Sender address verification reporting ------------------------------------------- -See section 42.44 for a general discussion of verification. When sender +See section 43.44 for a general discussion of verification. When sender verification fails in an ACL, the details of the failure are given as additional output lines before the 550 response to the relevant SMTP command (RCPT or DATA). For example, if sender callout is in use, you might see: @@ -27909,7 +28393,7 @@ verify = sender/no_details -42.49 Redirection while verifying +43.49 Redirection while verifying --------------------------------- A dilemma arises when a local address is redirected by aliasing or forwarding @@ -27952,7 +28436,7 @@ address and a report is output for each of them. -42.50 Client SMTP authorization (CSA) +43.50 Client SMTP authorization (CSA) ------------------------------------- Client SMTP Authorization is a system that allows a site to advertise which @@ -28022,7 +28506,7 @@ authorization required but absent, or "?" for unknown. -42.51 Bounce address tag validation +43.51 Bounce address tag validation ----------------------------------- Bounce address tag validation (BATV) is a scheme whereby the envelope senders @@ -28105,7 +28589,7 @@ If no key can be found for the existing return path, no signing takes place. -42.52 Using an ACL to control relaying +43.52 Using an ACL to control relaying -------------------------------------- An MTA is said to relay a message if it receives it from some host and delivers @@ -28160,7 +28644,7 @@ in chapter 7. -42.53 Checking a relay configuration +43.53 Checking a relay configuration ------------------------------------ You can check the relay characteristics of your configuration in the same way @@ -28178,7 +28662,7 @@ =============================================================================== -43. CONTENT SCANNING AT ACL TIME +44. CONTENT SCANNING AT ACL TIME The extension of Exim to include content scanning at ACL time, formerly known as "exiscan", was originally implemented as a patch by Tom Kistner. The code @@ -28187,7 +28671,7 @@ specification. It is also possible to scan the content of messages at other times. The -local_scan() function (see chapter 44) allows for content scanning after all +local_scan() function (see chapter 45) allows for content scanning after all the ACLs have run. A transport filter can be used to scan messages at delivery time (see the transport_filter option, described in chapter 24). @@ -28238,7 +28722,7 @@ same directory by default. -43.1 Scanning for viruses +44.1 Scanning for viruses ------------------------- The malware ACL condition lets you connect virus scanner software to Exim. It @@ -28246,8 +28730,11 @@ specialized interfaces for "daemon" type virus scanners, which are resident in memory and thus are much faster. -You can set the av_scanner option in first part of the Exim configuration file -to specify which scanner to use, together with any additional options that are +A timeout of 2 minutes is applied to a scanner call (by default); if it expires +then a defer action is taken. + +You can set the av_scanner option in the main part of the configuration to +specify which scanner to use, together with any additional options that are needed. The basic syntax is as follows: av_scanner = :::[...] @@ -28260,6 +28747,31 @@ before use. The usual list-parsing of the content (see 6.19) applies. The following scanner types are supported in this release: +avast + + This is the scanner daemon of Avast. It has been tested with Avast Core + Security (currenty at version 1.1.7). You can get a trial version at http:/ + /www.avast.com or for Linux at http://www.avast.com/linux-server-antivirus. + This scanner type takes one option, which can be either a full path to a + UNIX socket, or host and port specifiers separated by white space. The host + may be a name or an IP address; the port is either a single number or a + pair of numbers with a dash between. Any further options are given, on + separate lines, to the daemon as options before the main scan command. For + example: + + av_scanner = avast:/var/run/avast/scan.sock:FLAGS -fullfiles:SENSITIVITY -pup + av_scanner = avast:192.168.2.22 5036 + + If you omit the argument, the default path /var/run/avast/scan.sock is + used. If you use a remote host, you need to make Exim's spool directory + available to it, as the scanner is passed a file path, not file contents. + For information about available commands and their options you may use + + $ socat UNIX:/var/run/avast/scan.sock STDIO: + FLAGS + SENSITIVITY + PACK + aveserver This is the scanner daemon of Kaspersky Version 5. You can get a trial @@ -28274,17 +28786,34 @@ This daemon-type scanner is GPL and free. You can get it at http:// www.clamav.net/. Some older versions of clamd do not seem to unpack MIME containers, so it used to be recommended to unpack MIME attachments in the - MIME ACL. This no longer believed to be necessary. One option is required: - either the path and name of a UNIX socket file, or a hostname or IP number, - and a port, separated by space, as in the second of these examples: + MIME ACL. This is no longer believed to be necessary. + + The options are a list of server specifiers, which may be a UNIX socket + specification, a TCP socket specification, or a (global) option. + + A socket specification consists of a space-separated list. For a Unix + socket the first element is a full path for the socket, for a TCP socket + the first element is the IP address and the second a port number, Any + further elements are per-server (non-global) options. These per-server + options are supported: + + retry= Retry on connect fail + + The "retry" option specifies a time after which a single retry for a failed + connect is made. The default is to not retry. + + If a Unix socket file is specified, only one server is supported. + + Examples: av_scanner = clamd:/opt/clamd/socket av_scanner = clamd:192.0.2.3 1234 av_scanner = clamd:192.0.2.3 1234:local + av_scanner = clamd:192.0.2.3 1234 retry=10s av_scanner = clamd:192.0.2.3 1234 : 192.0.2.4 1234 If the value of av_scanner points to a UNIX socket file or contains the - local keyword, then the ClamAV interface will pass a filename containing + "local" option, then the ClamAV interface will pass a filename containing the data to be scanned, which will should normally result in less I/O happening and be more efficient. Normally in the TCP case, the data is streamed to ClamAV as Exim does not assume that there is a common @@ -28343,9 +28872,10 @@ drweb - The DrWeb daemon scanner (http://www.sald.com/) interface takes one - argument, either a full path to a UNIX socket, or an IP address and port - separated by white space, as in these examples: + The DrWeb daemon scanner (http://www.sald.com/) interface takes one option, + either a full path to a UNIX socket, or host and port specifiers separated + by white space. The host may be a name or an IP address; the port is either + a single number or a pair of numbers with a dash between. For example: av_scanner = drweb:/var/run/drwebd.sock av_scanner = drweb:192.168.2.20 31337 @@ -28353,6 +28883,16 @@ If you omit the argument, the default path /usr/local/drweb/run/drwebd.sock is used. Thanks to Alex Miller for contributing the code for this scanner. +f-protd + + The f-protd scanner is accessed via HTTP over TCP. One argument is taken, + being a space-separated hostname and port number (or port-range). For + example: + + av_scanner = f-protd:localhost 10200-10204 + + If you omit the argument, the default values show above are used. + fsecure The F-Secure daemon scanner (http://www.f-secure.com) takes one argument @@ -28423,8 +28963,11 @@ using expandable items in av_scanner disables this caching, in which case each use of the malware condition causes a new scan of the message. -The malware condition takes a right-hand argument that is expanded before use. -It can then be one of +The malware condition takes a right-hand argument that is expanded before + +use and taken as a list, slash-separated by default. + +The first element can then be one of * "true", "*", or "1", in which case the message is scanned for viruses. The condition succeeds if a virus was found, and fail otherwise. This is the @@ -28438,9 +28981,19 @@ expression. This allows you to take special actions on certain types of virus. -You can append "/defer_ok" to the malware condition to accept messages even if -there is a problem with the virus scanner. Otherwise, such a problem causes the -ACL to defer. + Note that "/" characters in the RE must be doubled due to the + list-processing, unless the separator is changed (in the usual way). + +You can append a "defer_ok" element to the malware argument list to accept +messages even if there is a problem with the virus scanner. Otherwise, such a +problem causes the ACL to defer. + +You can append a "tmo=" element to the malware argument list to specify a +non-default timeout. The default is two minutes. For example: + +malware = * / defer_ok / tmo=10s + +A timeout causes the ACL to defer. When a virus is found, the condition sets up an expansion variable called $malware_name that contains the name of the virus. You can use it in a message @@ -28448,7 +29001,7 @@ data. If your virus scanner cannot unpack MIME and TNEF containers itself, you should -use the demime condition (see section 43.6) before the malware condition. +use the demime condition (see section 44.6) before the malware condition. Beware the interaction of Exim's message_size_limit with any size limits imposed by your anti-virus scanner. @@ -28481,13 +29034,19 @@ malware = * -43.2 Scanning with SpamAssassin -------------------------------- +44.2 Scanning with SpamAssassin and Rspamd +------------------------------------------ The spam ACL condition calls SpamAssassin's spamd daemon to get a spam score -and a report for the message. You can get SpamAssassin at http:// -www.spamassassin.org, or, if you have a working Perl installation, you can use -CPAN by running: +and a report for the message. + +Support is also provided for Rspamd. + +For more information about installation and configuration of SpamAssassin or +Rspamd refer to their respective websites at http://spamassassin.apache.org and +http://www.rspamd.com + +SpamAssassin can be installed with CPAN by running: perl -MCPAN -e 'install Mail::SpamAssassin' @@ -28495,42 +29054,83 @@ documentation to see how you can tweak it. The default installation should work nicely, however. -After having installed and configured SpamAssassin, start the spamd daemon. By -default, it listens on 127.0.0.1, TCP port 783. If you use another host or port -for spamd, you must set the spamd_address option in the global part of the Exim -configuration as follows (example): +By default, SpamAssassin listens on 127.0.0.1, TCP port 783 and if you intend +to use an instance running on the local host you do not need to set +spamd_address. If you intend to use another host or port for SpamAssassin, you +must set the spamd_address option in the global part of the Exim configuration +as follows (example): spamd_address = 192.168.99.45 387 -You do not need to set this option if you use the default. As of version 2.60, -spamd also supports communication over UNIX sockets. If you want to use these, -supply spamd_address with an absolute file name instead of a address/port pair: +To use Rspamd (which by default listens on all local addresses on TCP port +11333) you should add variant=rspamd after the address/port pair, for example: + +spamd_address = 127.0.0.1 11333 variant=rspamd + +As of version 2.60, SpamAssassin also supports communication over UNIX sockets. +If you want to us these, supply spamd_address with an absolute file name +instead of an address/port pair: spamd_address = /var/run/spamd_socket You can have multiple spamd servers to improve scalability. These can reside on other hardware reachable over the network. To specify multiple spamd servers, put multiple address/port pairs in the spamd_address option, separated with -colons: +colons (the separator can be changed in the usual way): spamd_address = 192.168.2.10 783 : \ 192.168.2.11 783 : \ 192.168.2.12 783 -Up to 32 spamd servers are supported. The servers are queried in a random -fashion. When a server fails to respond to the connection attempt, all other -servers are tried until one succeeds. If no server responds, the spam condition -defers. +Up to 32 spamd servers are supported. When a server fails to respond to the +connection attempt, all other servers are tried until one succeeds. If no +server responds, the spam condition defers. + +Unix and TCP socket specifications may be mixed in any order. Each element of +the list is a list itself, space-separated by default and changeable in the +usual way. + +For TCP socket specifications a host name or IP (v4 or v6, but subject to +list-separator quoting rules) address can be used, and the port can be one or a +dash-separated pair. In the latter case, the range is tried in strict order. + +Elements after the first for Unix sockets, or second for TCP socket, are +options. The supported option are: + +pri= Selection priority +weight= Selection bias +time=- Use only between these times of day +retry= Retry on connect fail +tmo= Connection time limit +variant=rspamd Use Rspamd rather than SpamAssassin protocol + +The "pri" option specifies a priority for the server within the list, higher +values being tried first. The deafult priority is 1. + +The "weight" option specifies a selection bias. Within a priority set servers +are queried in a random fashion, weighted by this value. The default value for +selection bias is 1. + +Time specifications for the "time" option are .. in the +local time zone; each element being one or more digits. Either the seconds or +both minutes and seconds, plus the leading "." characters, may be omitted and +will be taken as zero. -Warning: It is not possible to use the UNIX socket connection method with -multiple spamd servers. +Timeout specifications for the "retry" and "tmo" options are the usual Exim +time interval standard, eg. "20s" or "1m". + +The "tmo" option specifies an overall timeout for communication. The default +value is two minutes. + +The "retry" option specifies a time after which a single retry for a failed +connect is made. The default is to not retry. The spamd_address variable is expanded before use if it starts with a dollar sign. In this case, the expansion may return a string that is used as the list so that multiple spamd servers can be the result of an expansion. -43.3 Calling SpamAssassin from an Exim ACL +44.3 Calling SpamAssassin from an Exim ACL ------------------------------------------ Here is a simple example of the use of the spam condition in a DATA ACL: @@ -28541,14 +29141,21 @@ The right-hand side of the spam condition specifies a name. This is relevant if you have set up multiple SpamAssassin profiles. If you do not want to scan using a specific profile, but rather use the SpamAssassin system-wide default -profile, you can scan for an unknown name, or simply use "nobody". However, you -must put something on the right-hand side. +profile, you can scan for an unknown name, or simply use "nobody". + +Rspamd does not use this setting. However, you must put something on the +right-hand side. The name allows you to use per-domain or per-user antispam profiles in principle, but this is not straightforward in practice, because a message may have multiple recipients, not necessarily all in the same domain. Because the -spam condition has to be called from a DATA ACL in order to be able to read the -contents of the message, the variables $local_part and $domain are not set. +spam condition has to be called from a DATA-time ACL in order to be able to +read the contents of the message, the variables $local_part and $domain are not +set. + +Careful enforcement of single-recipient messages (eg. by responding with defer +in the recipient ACL for all recipients after the first), or the use of PRDR, +are needed to use this feature. The right-hand side of the spam condition is expanded before being used, so you can put lookups or conditions there. When the right-hand side evaluates to "0" @@ -28569,8 +29176,11 @@ always return "true" by appending ":true" to the username. When the spam condition is run, it sets up a number of expansion variables. -These variables are saved with the received message, thus they are available -for use at delivery time. + +Except for $spam_report, + +these variables are saved with the received message so are available for use at +delivery time. $spam_score @@ -28596,6 +29206,13 @@ A multiline text table, containing the full SpamAssassin report for the message. Useful for inclusion in headers or reject messages. + This variable is only usable in a DATA-time ACL. + +$spam_action + + For SpamAssassin either 'reject' or 'no action' depending on the spam score + versus threshold. For Rspamd, the recommended action. + The spam condition caches its results unless expansion in spamd_address was used. If you call it again with the same user name, it does not scan again, but rather returns the same values as before. @@ -28628,7 +29245,7 @@ condition = ${if >{$spam_score_int}{120}{1}{0}} -43.4 Scanning MIME parts +44.4 Scanning MIME parts ------------------------ The acl_smtp_mime global option specifies an ACL that is called once for each @@ -28649,7 +29266,7 @@ You cannot use the malware or spam conditions in a MIME ACL; these can only be used in the DATA or non-SMTP ACLs. However, you can use the regex condition to match against the raw MIME part. You can also use the mime_regex condition to -match against the decoded MIME part (see section 43.5). +match against the decoded MIME part (see section 44.5). At the start of a MIME ACL, a number of variables are set from the header information for the relevant MIME part. These are described below. The contents @@ -28694,7 +29311,7 @@ The MIME ACL supports the regex and mime_regex conditions. These can be used to match regular expressions against raw and decoded MIME parts, respectively. -They are described in section 43.5. +They are described in section 44.5. The following list describes all expansion variables that are available in the MIME ACL: @@ -28775,6 +29392,9 @@ This is perhaps the most important of the MIME variables. It contains a proposed filename for an attachment, if one was found in either the Content-Type: or Content-Disposition: headers. The filename will be RFC2047 + + or RFC2231 + decoded, but no additional sanity checks are done. If no filename was found, this variable contains the empty string. @@ -28833,7 +29453,7 @@ -1. -43.5 Scanning with regular expressions +44.5 Scanning with regular expressions -------------------------------------- You can specify your own custom regular expression matches on the full body of @@ -28868,7 +29488,7 @@ Warning: With large messages, these conditions can be fairly CPU-intensive. -43.6 The demime condition +44.6 The demime condition ------------------------- The demime ACL condition provides MIME unpacking, sanity checking and file @@ -28946,16 +29566,16 @@ =============================================================================== -44. ADDING A LOCAL SCAN FUNCTION TO EXIM +45. ADDING A LOCAL SCAN FUNCTION TO EXIM In these days of email worms, viruses, and ever-increasing spam, some sites want to apply a lot of checking to messages before accepting them. -The content scanning extension (chapter 43) has facilities for passing messages +The content scanning extension (chapter 44) has facilities for passing messages to external virus and spam scanning software. You can also do a certain amount in Exim itself through string expansions and the condition condition in the ACL that runs after the SMTP DATA command or the ACL for non-SMTP messages (see -chapter 42), but this has its limitations. +chapter 43), but this has its limitations. To allow for further customization to a site's own requirements, there is the possibility of linking Exim with a private message scanning function, written @@ -28977,7 +29597,7 @@ ends with a non-zero code. The incident is logged on the main and reject logs. -44.1 Building Exim to use a local scan function +45.1 Building Exim to use a local scan function ----------------------------------------------- To make use of the local scan function feature, you must tell Exim where your @@ -28999,10 +29619,10 @@ LOCAL_SCAN_HAS_OPTIONS=yes -in Local/Makefile (see section 44.3 below). +in Local/Makefile (see section 45.3 below). -44.2 API for local_scan() +45.2 API for local_scan() ------------------------- You must include this line near the start of your code: @@ -29081,7 +29701,7 @@ message is not written to the reject log. It has the effect of unsetting the rejected_header log selector for just this rejection. If rejected_header is already unset (see the discussion of the log_selection - option in section 51.15), this code is the same as LOCAL_SCAN_REJECT. + option in section 52.15), this code is the same as LOCAL_SCAN_REJECT. "LOCAL_SCAN_TEMPREJECT_NOLOGHDR" @@ -29093,7 +29713,7 @@ command line options. -44.3 Configuration options for local_scan() +45.3 Configuration options for local_scan() ------------------------------------------- It is possible to have option settings in the main configuration file that set @@ -29182,7 +29802,7 @@ values of all the local_scan() options. -44.4 Available Exim variables +45.4 Available Exim variables ----------------------------- The header local_scan.h gives you access to a number of C variables. These are @@ -29312,10 +29932,10 @@ int store_pool The contents of this variable control which pool of memory is used for new - requests. See section 44.8 for details. + requests. See section 45.8 for details. -44.5 Structure of header lines +45.5 Structure of header lines ------------------------------ The header_line structure contains the members listed below. You can add @@ -29329,7 +29949,7 @@ int type A code identifying certain headers that Exim recognizes. The codes are - printing characters, and are documented in chapter 55 of this manual. + printing characters, and are documented in chapter 56 of this manual. Notice in particular that any header line whose type is * is not transmitted with the message. This flagging is used for header lines that have been rewritten, or are to be removed (for example, Envelope-sender: @@ -29346,7 +29966,7 @@ followed by a zero byte. Internal newlines are preserved. -44.6 Structure of recipient items +45.6 Structure of recipient items --------------------------------- The recipient_item structure contains these members: @@ -29372,7 +29992,7 @@ field is NULL for all recipients. -44.7 Available Exim functions +45.7 Available Exim functions ----------------------------- The header local_scan.h gives you access to a number of Exim functions. These @@ -29465,7 +30085,7 @@ failure. If expansion does not change the string, the return value is the pointer to the input string. Otherwise, the return value points to a new block of memory that was obtained by a call to store_get(). See section - 44.8 below for a discussion of memory handling. + 45.8 below for a discussion of memory handling. void header_add(int type, char *format, ...) @@ -29705,7 +30325,7 @@ See the next section for more discussion. -44.8 More about Exim's memory handling +45.8 More about Exim's memory handling -------------------------------------- No function is provided for freeing memory, because that is never needed. The @@ -29737,7 +30357,7 @@ =============================================================================== -45. SYSTEM-WIDE MESSAGE FILTERING +46. SYSTEM-WIDE MESSAGE FILTERING The previous chapters (on ACLs and the local scan function) describe checks that can be applied to messages before they are accepted by a host. There is @@ -29761,11 +30381,11 @@ to individual recipient addresses, such as $local_part and $domain, are not set, and the "personal" condition is not meaningful. If you want to run a centrally-specified filter for each recipient address independently, you can do -so by setting up a suitable redirect router, as described in section 45.8 +so by setting up a suitable redirect router, as described in section 46.8 below. -45.1 Specifying a system filter +46.1 Specifying a system filter ------------------------------- The name of the file that contains the system filter must be specified by @@ -29783,7 +30403,7 @@ any messages generated by the reply command. -45.2 Testing a system filter +46.2 Testing a system filter ---------------------------- You can run simple tests of a system filter in the same way as for a user @@ -29794,7 +30414,7 @@ you can use both -bF and -bf on the same command line. -45.3 Contents of a system filter +46.3 Contents of a system filter -------------------------------- The language used to specify system filters is the same as for users' filter @@ -29822,7 +30442,7 @@ filter files can refer. -45.4 Additional variable for system filters +46.4 Additional variable for system filters ------------------------------------------- The expansion variable $recipients, containing a list of all the recipients of @@ -29830,7 +30450,7 @@ filters. It is not available in users' filters for privacy reasons. -45.5 Defer, freeze, and fail commands for system filters +46.5 Defer, freeze, and fail commands for system filters -------------------------------------------------------- There are three extra commands (defer, freeze and fail) which are always @@ -29891,7 +30511,7 @@ take place. -45.6 Adding and removing headers in a system filter +46.6 Adding and removing headers in a system filter --------------------------------------------------- Two filter commands that are available only in system filters are: @@ -29936,7 +30556,7 @@ that are added by a system filter are visible to users' filter files and to all routers and transports. This contrasts with the manipulation of header lines by routers and transports, which is not immediate, but which instead is saved up -until the message is actually being written (see section 46.17). +until the message is actually being written (see section 47.17). If the message is not delivered at the first attempt, header lines that were added by the system filter are stored with the message, and so are still @@ -29956,7 +30576,7 @@ headers remove "Old-Subject" -45.7 Setting an errors address in a system filter +46.7 Setting an errors address in a system filter ------------------------------------------------- In a system filter, if a deliver command is followed by @@ -29974,7 +30594,7 @@ address if its delivery failed. -45.8 Per-address filtering +46.8 Per-address filtering -------------------------- In contrast to the system filter, which is run just once per message for each @@ -30007,7 +30627,7 @@ =============================================================================== -46. MESSAGE PROCESSING +47. MESSAGE PROCESSING Exim performs various transformations on the sender and recipient addresses of all messages that it handles, and also on the messages' header lines. Some of @@ -30030,7 +30650,7 @@ that there are appropriate entries in your ACLs. -46.1 Submission mode for non-local messages +47.1 Submission mode for non-local messages ------------------------------------------- Processing that happens automatically for locally-originated messages (unless @@ -30040,8 +30660,8 @@ control = submission -in a MAIL, RCPT, or pre-data ACL for an incoming message (see sections 42.21 -and 42.22). This makes Exim treat the message as a local submission, and is +in a MAIL, RCPT, or pre-data ACL for an incoming message (see sections 43.21 +and 43.22). This makes Exim treat the message as a local submission, and is normally used when the source of the message is known to be an MUA running on a client host (as opposed to an MTA). For example, to set submission mode for messages originating on the IPv4 loopback interface, you could include the @@ -30068,8 +30688,8 @@ control = submission/domain=some.domain -The domain may be empty. How this value is used is described in sections 46.11 -and 46.16. There is also a name option that allows you to specify the user's +The domain may be empty. How this value is used is described in sections 47.11 +and 47.16. There is also a name option that allows you to specify the user's full name for inclusion in a created Sender: or From: header line. For example: accept authenticated = * @@ -30100,7 +30720,7 @@ spoof another's address. -46.2 Line endings +47.2 Line endings ----------------- RFC 2821 specifies that CRLF (two characters: carriage-return, followed by @@ -30138,7 +30758,7 @@ header line. -46.3 Unqualified addresses +47.3 Unqualified addresses -------------------------- By default, Exim expects every envelope address it receives from an external @@ -30161,7 +30781,7 @@ and recipient_unqualified_hosts, -46.4 The UUCP From line +47.4 The UUCP From line ----------------------- Messages that have come from UUCP (and some other applications) often begin @@ -30198,7 +30818,7 @@ SMTP message from a source that is not permitted to send them. -46.5 Resent- header lines +47.5 Resent- header lines ------------------------- RFC 2822 makes provision for sets of header lines starting with the string @@ -30231,7 +30851,7 @@ Resent- header lines are present. -46.6 The Auto-Submitted: header line +47.6 The Auto-Submitted: header line ------------------------------------ Whenever Exim generates an autoreply, a bounce, or a delay warning message, it @@ -30240,7 +30860,7 @@ Auto-Submitted: auto-replied -46.7 The Bcc: header line +47.7 The Bcc: header line ------------------------- If Exim is called with the -t option, to take recipient addresses from a @@ -30249,7 +30869,7 @@ existing Bcc: is not removed. -46.8 The Date: header line +47.8 The Date: header line -------------------------- If a locally-generated or submission-mode message has no Date: header line, @@ -30257,7 +30877,7 @@ suppress_local_fixups control has been specified. -46.9 The Delivery-date: header line +47.9 The Delivery-date: header line ----------------------------------- Delivery-date: header lines are not part of the standard RFC 2822 header set. @@ -30267,7 +30887,7 @@ (the default), Exim removes Delivery-date: header lines from incoming messages. -46.10 The Envelope-to: header line +47.10 The Envelope-to: header line ---------------------------------- Envelope-to: header lines are not part of the standard RFC 2822 header set. @@ -30277,7 +30897,7 @@ default), Exim removes Envelope-to: header lines from incoming messages. -46.11 The From: header line +47.11 The From: header line --------------------------- If a submission-mode message does not contain a From: header line, Exim adds @@ -30302,17 +30922,17 @@ If a locally-generated incoming message does not contain a From: header line, and the suppress_local_fixups control is not set, Exim adds one containing the sender's address. The calling user's login name and full name are used to -construct the address, as described in section 46.18. They are obtained from +construct the address, as described in section 47.18. They are obtained from the password data by calling getpwuid() (but see the unknown_login configuration option). The address is qualified with qualify_domain. For compatibility with Sendmail, if an incoming, non-SMTP message has a From: header line containing just the unqualified login name of the calling user, this is replaced by an address containing the user's login name and full name -as described in section 46.18. +as described in section 47.18. -46.12 The Message-ID: header line +47.12 The Message-ID: header line --------------------------------- If a locally-generated or submission-mode incoming message does not contain a @@ -30325,7 +30945,7 @@ message_id_header_text and/or message_id_header_domain options. -46.13 The Received: header line +47.13 The Received: header line ------------------------------- A Received: header line is added at the start of every message. The contents @@ -30342,7 +30962,7 @@ -H spool file is written) the earliest time at which delivery could start. -46.14 The References: header line +47.14 The References: header line --------------------------------- Messages created by the autoreply transport include a References: header line. @@ -30356,7 +30976,7 @@ message ID of the incoming message. -46.15 The Return-path: header line +47.15 The Return-path: header line ---------------------------------- Return-path: header lines are defined as something an MTA may insert when it @@ -30366,7 +30986,7 @@ Return-path: header lines from incoming messages. -46.16 The Sender: header line +47.16 The Sender: header line ----------------------------- For a locally-originated message from an untrusted user, Exim may remove an @@ -30416,14 +31036,14 @@ in the case of submission mode when sender_retain is specified. -46.17 Adding and removing header lines in routers and transports +47.17 Adding and removing header lines in routers and transports ---------------------------------------------------------------- When a message is delivered, the addition and removal of header lines can be specified in a system filter, or on any of the routers and transports that -process the message. Section 45.6 contains details about modifying headers in a +process the message. Section 46.6 contains details about modifying headers in a system filter. Header lines can also be added in an ACL as a message is -received (see section 42.24). +received (see section 43.24). In contrast to what happens in a system filter, header modifications that are specified on routers and transports apply only to the particular recipient @@ -30458,7 +31078,8 @@ Multiple headers_remove options for a single router or transport can be specified; the arguments will append to a single header-names list. Each item -is separately expanded. +is separately expanded. Note that colons in complex expansions which are used +to form all or part of a headers_remove list will act as list separators. When headers_add or headers_remove is specified on a router, items are expanded at routing time, and then associated with all addresses that are accepted by @@ -30514,7 +31135,7 @@ redirect router that has the one_time option set. -46.18 Constructed addresses +47.18 Constructed addresses --------------------------- When Exim constructs a sender address for a locally-generated message, it uses @@ -30545,7 +31166,7 @@ with codes greater than 127) count as printing characters or not. -46.19 Case of local parts +47.19 Case of local parts ------------------------- RFC 2822 states that the case of letters in the local parts of addresses cannot @@ -30575,7 +31196,7 @@ with the correct case in a case-sensitive manner. -46.20 Dots in local parts +47.20 Dots in local parts ------------------------- RFC 2822 forbids empty components in local parts. That is, an unquoted local @@ -30584,7 +31205,7 @@ empty components for compatibility. -46.21 Rewriting addresses +47.21 Rewriting addresses ------------------------- Rewriting of sender and recipient addresses, and addresses in headers, can @@ -30617,7 +31238,7 @@ =============================================================================== -47. SMTP PROCESSING +48. SMTP PROCESSING Exim supports a number of different ways of using the SMTP protocol, and its LMTP variant, which is an interactive protocol for transferring messages into a @@ -30648,7 +31269,7 @@ to contain the envelope information. -47.1 Outgoing SMTP and LMTP over TCP/IP +48.1 Outgoing SMTP and LMTP over TCP/IP --------------------------------------- Outgoing SMTP and LMTP over TCP/IP is implemented by the smtp transport. The @@ -30668,7 +31289,7 @@ If the remote server advertises support for the STARTTLS command, and Exim was built to support TLS encryption, it tries to start a TLS session unless the -server matches hosts_avoid_tls. See chapter 41 for more details. Either a match +server matches hosts_avoid_tls. See chapter 42 for more details. Either a match in that or hosts_verify_avoid_tls apply when the transport is called for verification. @@ -30717,7 +31338,7 @@ square bracket of the IP address. -47.2 Errors in outgoing SMTP +48.2 Errors in outgoing SMTP ---------------------------- Three different kinds of error are recognized for outgoing SMTP: host errors, @@ -30847,7 +31468,7 @@ error, in order not to delay other messages to the same host. -47.3 Incoming SMTP messages over TCP/IP +48.3 Incoming SMTP messages over TCP/IP --------------------------------------- Incoming SMTP messages can be accepted in one of two ways: by running a @@ -30930,7 +31551,7 @@ available with inetd. Exim can be configured to verify addresses in incoming SMTP commands as they -are received. See chapter 42 for details. It can also be configured to rewrite +are received. See chapter 43 for details. It can also be configured to rewrite addresses at this time - before any syntax checking is done. See section 31.9. Exim can also be configured to limit the rate at which a client host submits @@ -30938,7 +31559,7 @@ option. -47.4 Unrecognized SMTP commands +48.4 Unrecognized SMTP commands ------------------------------- If Exim receives more than smtp_max_unknown_commands unrecognized SMTP commands @@ -30949,7 +31570,7 @@ circumstances, a number of non-SMTP lines are sent first. -47.5 Syntax and protocol errors in SMTP commands +48.5 Syntax and protocol errors in SMTP commands ------------------------------------------------ A syntax error is detected if an SMTP command is recognized, but there is @@ -30962,7 +31583,7 @@ loop sending bad commands (yes, it has been seen). -47.6 Use of non-mail SMTP commands +48.6 Use of non-mail SMTP commands ---------------------------------- The "non-mail" SMTP commands are those other than MAIL, RCPT, and DATA. Exim @@ -30988,7 +31609,7 @@ you can exclude any specific badly-behaved hosts that you have to live with. -47.7 The VRFY and EXPN commands +48.7 The VRFY and EXPN commands ------------------------------- When Exim receives a VRFY or EXPN command on a TCP/IP connection, it runs the @@ -31007,7 +31628,7 @@ failures are logged on the main log for consistency with RCPT failures. -47.8 The ETRN command +48.8 The ETRN command --------------------- RFC 1985 describes an SMTP command called ETRN that is designed to overcome the @@ -31059,7 +31680,7 @@ to change them before running the command. -47.9 Incoming local SMTP +48.9 Incoming local SMTP ------------------------ Some user agents use SMTP to pass messages to their local MTA using the @@ -31077,7 +31698,7 @@ This accepts SMTP messages from local processes without doing any other tests. -47.10 Outgoing batched SMTP +48.10 Outgoing batched SMTP --------------------------- Both the appendfile and pipe transports can be used for handling batched SMTP. @@ -31122,7 +31743,7 @@ (unless there are more than 1000 recipients). -47.11 Incoming batched SMTP +48.11 Incoming batched SMTP --------------------------- The -bS command line option causes Exim to accept one or more messages by @@ -31168,7 +31789,7 @@ =============================================================================== -48. CUSTOMIZING BOUNCE AND WARNING MESSAGES +49. CUSTOMIZING BOUNCE AND WARNING MESSAGES When a message fails to be delivered, or remains on the queue for more than a configured amount of time, Exim sends a message to the original sender, or to @@ -31185,7 +31806,7 @@ to all warning and bounce messages, -48.1 Customizing bounce messages +49.1 Customizing bounce messages -------------------------------- If bounce_message_text is set, its contents are included in the default message @@ -31217,13 +31838,8 @@ * The third item is used to introduce any text from pipe transports that is to be returned to the sender. It is omitted if there is no such text. - * The fourth item is used to introduce the copy of the message that is - returned as part of the error report. - - * The fifth item is added after the fourth one if the returned message is - truncated because it is bigger than return_size_limit. - - * The sixth item is added after the copy of the original message. + * The fourth, fifth and sixth items will be ignored and may be empty. The + fields exist for back-compatibility The default state (bounce_message_file unset) is equivalent to the following file, in which the sixth item is empty. The Subject: and some other lines have @@ -31254,7 +31870,7 @@ **** -48.2 Customizing warning messages +49.2 Customizing warning messages --------------------------------- The option warn_message_file can be pointed at a template file for use when @@ -31307,13 +31923,13 @@ =============================================================================== -49. SOME COMMON CONFIGURATION SETTINGS +50. SOME COMMON CONFIGURATION SETTINGS This chapter discusses some configuration settings that seem to be fairly common. More examples and discussion can be found in the Exim book. -49.1 Sending mail to a smart host +50.1 Sending mail to a smart host --------------------------------- If you want to send all mail for non-local domains to a "smart host", you @@ -31328,10 +31944,10 @@ You can use the smart host's IP address instead of the name if you wish. If you are using Exim only to submit messages to a smart host, and not for receiving incoming messages, you can arrange for it to do the submission synchronously by -setting the mua_wrapper option (see chapter 50). +setting the mua_wrapper option (see chapter 51). -49.2 Using Exim to handle mailing lists +50.2 Using Exim to handle mailing lists --------------------------------------- Exim can be used to run simple mailing lists, but for large and/or complicated @@ -31376,7 +31992,7 @@ request, are also possible. -49.3 Syntax errors in mailing lists +50.3 Syntax errors in mailing lists ----------------------------------- If an entry in redirection data contains a syntax error, Exim normally defers @@ -31392,7 +32008,7 @@ syntax_errors_to to the same address as errors_to. -49.4 Re-expansion of mailing lists +50.4 Re-expansion of mailing lists ---------------------------------- Exim remembers every individual address to which a message has been delivered, @@ -31420,7 +32036,7 @@ level of expansion anyway. -49.5 Closed mailing lists +50.5 Closed mailing lists ------------------------- The examples so far have assumed open mailing lists, to which anybody may send @@ -31474,7 +32090,7 @@ the address, giving a suitable error message. -49.6 Variable Envelope Return Paths (VERP) +50.6 Variable Envelope Return Paths (VERP) ------------------------------------------ Variable Envelope Return Paths - see http://cr.yp.to/proto/verp.txt - are a way @@ -31562,7 +32178,7 @@ used). -49.7 Virtual domains +50.7 Virtual domains -------------------- The phrase virtual domain is unfortunately used with two rather different @@ -31631,7 +32247,7 @@ information about the domains. -49.8 Multiple user mailboxes +50.8 Multiple user mailboxes ---------------------------- Heavy email users often want to operate with multiple mailboxes, into which @@ -31681,7 +32297,7 @@ as a default. -49.9 Simplified vacation processing +50.9 Simplified vacation processing ----------------------------------- The traditional way of running the vacation program is for a user to set up a @@ -31706,7 +32322,7 @@ use of arbitrary pipes by users is locked out. -49.10 Taking copies of mail +50.10 Taking copies of mail --------------------------- Some installations have policies that require archive copies of all messages to @@ -31720,7 +32336,7 @@ of delivery by sites that insist on doing such things. -49.11 Intermittently connected hosts +50.11 Intermittently connected hosts ------------------------------------ It has become quite common (because it is cheaper) for hosts to connect to the @@ -31733,7 +32349,7 @@ Nevertheless there are some features that can be used. -49.12 Exim on the upstream server host +50.12 Exim on the upstream server host -------------------------------------- It is tempting to arrange for incoming mail for the intermittently connected @@ -31761,7 +32377,7 @@ This stops a lot of failed delivery attempts from occurring, but Exim remembers which messages it has queued up for that host. Once the intermittent host comes online, forcing delivery of one message (either by using the -M or -R options, -or by using the ETRN SMTP command (see section 47.8) causes all the queued up +or by using the ETRN SMTP command (see section 48.8) causes all the queued up messages to be delivered, often down a single SMTP connection. While the host remains connected, any new messages get delivered immediately. @@ -31774,7 +32390,7 @@ separate transport for the intermittently connected ones. -49.13 Exim on the intermittently connected client host +50.13 Exim on the intermittently connected client host ------------------------------------------------------ The value of smtp_accept_queue_per_connection should probably be increased, or @@ -31794,7 +32410,7 @@ =============================================================================== -50. USING EXIM AS A NON-QUEUEING CLIENT +51. USING EXIM AS A NON-QUEUEING CLIENT On a personal computer, it is a common requirement for all email to be sent to a "smart host". There are plenty of MUAs that can be configured to operate that @@ -31883,13 +32499,13 @@ the message, failing if there is any kind of problem. Because no local deliveries are done and no daemon can be run, Exim does not need root privilege. It should be possible to run it setuid to exim instead of setuid to -root. See section 54.3 for a general discussion about the advantages and +root. See section 55.3 for a general discussion about the advantages and disadvantages of running without root privilege. =============================================================================== -51. LOG FILES +52. LOG FILES Exim writes three different logs, referred to as the main log, the reject log, and the panic log: @@ -31901,7 +32517,7 @@ in the main log. Some of them are optional, in which case the log_selector option controls whether they are included or not. A Perl script called eximstats, which does simple analysis of main log files, is provided in the - Exim distribution (see section 52.7). + Exim distribution (see section 53.7). * The reject log records information from messages that are rejected as a result of a configuration option (that is, for policy reasons). The first @@ -31947,12 +32563,12 @@ 2003-04-25 11:17:07 +0100 Start queue run: pid=12762 Exim does not include its process id in log lines by default, but you can -request that it does so by specifying the "pid" log selector (see section 51.15 +request that it does so by specifying the "pid" log selector (see section 52.15 ). When this is set, the process id is output, in square brackets, immediately after the time and date. -51.1 Where the logs are written +52.1 Where the logs are written ------------------------------- The logs may be written to local files, or to syslog, or both. However, it @@ -31993,11 +32609,12 @@ log_file_path = $spool_directory/log/%slog -If you do not specify anything at build time or run time, that is where the -logs are written. +If you do not specify anything at build time or run time, or if you unset the +option at run time (i.e. "log_file_path = "), that is where the logs are +written. A log file path may also contain "%D" or "%M" if datestamped log file names are -in use - see section 51.3 below. +in use - see section 52.3 below. Here are some examples of possible settings: @@ -32010,12 +32627,12 @@ error is logged. -51.2 Logging to local files that are periodically "cycled" +52.2 Logging to local files that are periodically "cycled" ---------------------------------------------------------- Some operating systems provide centralized and standardized methods for cycling log files. For those that do not, a utility script called exicyclog is provided -(see section 52.6). This renames and compresses the main and reject logs each +(see section 53.6). This renames and compresses the main and reject logs each time it is called. The maximum number of old logs to keep can be set. It is suggested this script is run as a daily cron job. @@ -32032,7 +32649,7 @@ some time, but no Exim processes should write to it once it has been renamed. -51.3 Datestamped log files +52.3 Datestamped log files -------------------------- Instead of cycling the main and reject log files by renaming them periodically, @@ -32073,7 +32690,7 @@ /var/log/exim/panic -51.4 Logging to syslog +52.4 Logging to syslog ---------------------- The use of syslog does not change what Exim logs or the format of its messages, @@ -32158,7 +32775,7 @@ is. -51.5 Log line flags +52.5 Log line flags ------------------- One line is written to the main log for each message received, and for each @@ -32175,7 +32792,7 @@ == delivery deferred; temporary problem -51.6 Logging message reception +52.6 Logging message reception ------------------------------ The format of the single-line entry in the main log that is written for every @@ -32240,10 +32857,10 @@ other). The log_selector option can be used to request the logging of additional data -when a message is received. See section 51.15 below. +when a message is received. See section 52.15 below. -51.7 Logging deliveries +52.7 Logging deliveries ----------------------- The format of the single-line entry in the main log that is written for every @@ -32290,10 +32907,10 @@ to the addressee, preceded by ">". The log_selector option can be used to request the logging of additional data -when a message is delivered. See section 51.15 below. +when a message is delivered. See section 52.15 below. -51.8 Discarded deliveries +52.8 Discarded deliveries ------------------------- When a message is discarded as a result of the command "seen finish" being @@ -32309,7 +32926,7 @@ R=blackhole_router -51.9 Deferred deliveries +52.9 Deferred deliveries ------------------------ When a delivery is deferred, a line of the following form is logged: @@ -32329,7 +32946,7 @@ appropriate value in log_selector. -51.10 Delivery failures +52.10 Delivery failures ----------------------- If a delivery fails because an address cannot be routed, a line of the @@ -32353,7 +32970,7 @@ "**". -51.11 Fake deliveries +52.11 Fake deliveries --------------------- If a delivery does not actually take place because the -N option has been used @@ -32361,7 +32978,7 @@ is replaced by "*>". -51.12 Completion +52.12 Completion ---------------- A line of the form @@ -32372,7 +32989,7 @@ at the end of its processing. -51.13 Summary of Fields in Log Lines +52.13 Summary of Fields in Log Lines ------------------------------------ A summary of the field identifiers that are used in log lines is shown in the @@ -32404,7 +33021,7 @@ X TLS cipher suite -51.14 Other log entries +52.14 Other log entries ----------------------- Various other types of log entry are written from time to time. Most should be @@ -32441,7 +33058,7 @@ failed. The delivery was discarded. -51.15 Reducing or increasing what is logged +52.15 Reducing or increasing what is logged ------------------------------------------- By setting the log_selector global option, you can disable some of Exim's @@ -32467,8 +33084,8 @@ *etrn ETRN commands *host_lookup_failed as it says ident_timeout timeout for ident connection - incoming_interface incoming interface on <= lines - incoming_port incoming port on <= lines + incoming_interface local interface on <= and => lines + incoming_port remote port on <= lines *lost_incoming_connection as it says (includes timeouts) outgoing_port add remote port to => lines *queue_run start and end queue runs @@ -32492,7 +33109,7 @@ smtp_protocol_error SMTP protocol errors smtp_syntax_error SMTP syntax errors subject contents of Subject: on <= lines - tls_certificate_verified certificate verification status +*tls_certificate_verified certificate verification status *tls_cipher TLS cipher suite on <= and => lines tls_peerdn TLS peer DN on <= and => lines tls_sni TLS SNI on <= lines @@ -32564,8 +33181,10 @@ * incoming_interface: The interface on which a message was received is added to the "<=" line as an IP address in square brackets, tagged by I= and followed by a colon and the port number. The local interface and port are - also added to other SMTP log lines, for example "SMTP connection from", and - to rejection lines. + also added to other SMTP log lines, for example "SMTP connection from" and + to rejection lines + + and (despite the name) the local interface is added to "=>" lines.. * incoming_port: The remote port number from which a message was received is added to log entries and Received: header lines, following the IP address @@ -32614,7 +33233,7 @@ * rejected_header: If a message's header has been received at the time a rejection is written to the reject log, the complete header is added to the log. Header logging can be turned off individually for messages that are - rejected by the local_scan() function (see section 44.2). + rejected by the local_scan() function (see section 45.2). * retry_defer: A log line is written if a delivery is deferred because a retry time has not yet been reached. However, this "retry time not reached" @@ -32738,7 +33357,7 @@ result of a list match is failure because a DNS lookup failed. -51.16 Message log +52.16 Message log ----------------- In addition to the general log files, Exim writes a log file for each message @@ -32757,33 +33376,33 @@ =============================================================================== -52. EXIM UTILITIES +53. EXIM UTILITIES A number of utility scripts and programs are supplied with Exim and are described in this chapter. There is also the Exim Monitor, which is covered in the next chapter. The utilities described here are: - 52.1 exiwhat list what Exim processes are doing - 52.2 exiqgrep grep the queue - 52.3 exiqsumm summarize the queue - 52.4 exigrep search the main log - 52.5 exipick select messages on various criteria - 52.6 exicyclog cycle (rotate) log files - 52.7 eximstats extract statistics from the log - 52.8 exim_checkaccess check address acceptance from given IP - 52.9 exim_dbmbuild build a DBM file - 52.10 exinext extract retry information - 52.11 exim_dumpdb dump a hints database - 52.11 exim_tidydb clean up a hints database - 52.11 exim_fixdb patch a hints database - 52.15 exim_lock lock a mailbox file + 53.1 exiwhat list what Exim processes are doing + 53.2 exiqgrep grep the queue + 53.3 exiqsumm summarize the queue + 53.4 exigrep search the main log + 53.5 exipick select messages on various criteria + 53.6 exicyclog cycle (rotate) log files + 53.7 eximstats extract statistics from the log + 53.8 exim_checkaccess check address acceptance from given IP + 53.9 exim_dbmbuild build a DBM file + 53.10 exinext extract retry information + 53.11 exim_dumpdb dump a hints database + 53.11 exim_tidydb clean up a hints database + 53.11 exim_fixdb patch a hints database + 53.15 exim_lock lock a mailbox file Another utility that might be of use to sites with many MTAs is Tom Kistner's exilog. It provides log visualizations across multiple Exim servers. See http:/ /duncanthrax.net/exilog/ for details. -52.1 Finding out what Exim processes are doing (exiwhat) +53.1 Finding out what Exim processes are doing (exiwhat) -------------------------------------------------------- On operating systems that can restart a system call after receiving a signal @@ -32823,7 +33442,7 @@ been split here, in order to fit it on the page. -52.2 Selective queue listing (exiqgrep) +53.2 Selective queue listing (exiqgrep) --------------------------------------- This utility is a Perl script contributed by Matt Hubbard. It runs @@ -32903,7 +33522,7 @@ There is one more option, -h, which outputs a list of options. -52.3 Summarizing the queue (exiqsumm) +53.3 Summarizing the queue (exiqsumm) ------------------------------------- The exiqsumm utility is a Perl script which reads the output of "exim -bp" and @@ -32935,7 +33554,7 @@ redirect router has been used to convert them into "top level" addresses). -52.4 Extracting specific information from the log (exigrep) +53.4 Extracting specific information from the log (exigrep) ----------------------------------------------------------- The exigrep utility is a Perl script that searches one or more main log files @@ -32947,7 +33566,7 @@ associated with a specific message, it is included in exigrep's output without any additional lines. The usage is: -exigrep [-t] [-I] [-l] [-v] [] ... +exigrep [-t] [-I] [-l] [-M] [-v] [] ... If no log file names are given on the command line, the standard input is read. @@ -32968,12 +33587,24 @@ The -v option inverts the matching condition. That is, a line is selected if it does not match the pattern. +The -M options means "related messages". exigrep will show messages that are +generated as a result/response to a message that exigrep matched normally. + +Example of -M: user_a sends a message to user_b, which generates a bounce back +to user_b. If exigrep is used to search for "user_a", only the first message +will be displayed. But if exigrep is used to search for "user_b", the first and +the second (bounce) message will be displayed. Using -M with exigrep when +searching for "user_a" will show both messages since the bounce is "related" to +or a "result" of the first message that was found by the search term. + If the location of a zcat command is known from the definition of ZCAT_COMMAND in Local/Makefile, exigrep automatically passes any file whose name ends in -COMPRESS_SUFFIX through zcat as it searches it. +COMPRESS_SUFFIX through zcat as it searches it. If the ZCAT_COMMAND is not +executable, exigrep tries to use autodetection of some well known compression +extensions. -52.5 Selecting messages by various criteria (exipick) +53.5 Selecting messages by various criteria (exipick) ----------------------------------------------------- John Jetmore's exipick utility is included in the Exim distribution. It lists @@ -32982,12 +33613,12 @@ ToolExipickManPage or run exipick with the --help option. -52.6 Cycling log files (exicyclog) +53.6 Cycling log files (exicyclog) ---------------------------------- The exicyclog script can be used to cycle (rotate) mainlog and rejectlog files. This is not necessary if only syslog is being used, or if you are using log -files with datestamps in their names (see section 51.3). Some operating systems +files with datestamps in their names (see section 52.3). Some operating systems have their own standard mechanisms for log cycling, and these can be used instead of exicyclog if preferred. There are two command line options for exicyclog: @@ -33023,7 +33654,7 @@ as root if you wish, but there is no need. -52.7 Mail statistics (eximstats) +53.7 Mail statistics (eximstats) -------------------------------- A Perl script called eximstats is provided for extracting statistical @@ -33085,7 +33716,7 @@ perldoc /usr/exim/bin/eximstats -52.8 Checking access policy (exim_checkaccess) +53.8 Checking access policy (exim_checkaccess) ---------------------------------------------- The -bh command line argument allows you to run a fake SMTP session with @@ -33124,7 +33755,7 @@ this is not yet available in a "packaged" form. -52.9 Making DBM files (exim_dbmbuild) +53.9 Making DBM files (exim_dbmbuild) ------------------------------------- The exim_dbmbuild program reads an input file containing keys and data in the @@ -33168,7 +33799,7 @@ doesn't actually make a new file, the return code is 2. -52.10 Finding individual retry times (exinext) +53.10 Finding individual retry times (exinext) ---------------------------------------------- A utility called exinext (mostly a Perl script) provides the ability to fish @@ -33193,7 +33824,7 @@ retry information for that local part in your default domain. A message id can be used to obtain retry information pertaining to a specific message. This exists only when an attempt to deliver a message to a remote host suffers a -message-specific error (see section 47.2). exinext is not particularly +message-specific error (see section 48.2). exinext is not particularly efficient, but then it is not expected to be run very often. The exinext utility calls Exim to find out information such as the location of @@ -33204,7 +33835,7 @@ environments where more than one configuration file is in use. -52.11 Hints database maintenance +53.11 Hints database maintenance -------------------------------- Three utility programs are provided for maintaining the DBM files that Exim @@ -33231,7 +33862,7 @@ smtp transport) -52.12 exim_dumpdb +53.12 exim_dumpdb ----------------- The entire contents of a database are written to the standard output by the @@ -33268,7 +33899,7 @@ cross-references. -52.13 exim_tidydb +53.13 exim_tidydb ----------------- The exim_tidydb utility program is used to tidy up the contents of a hints @@ -33316,7 +33947,7 @@ likely to keep on increasing. -52.14 exim_fixdb +53.14 exim_fixdb ---------------- The exim_fixdb program is a utility for interactively modifying databases. Its @@ -33339,7 +33970,7 @@ used as optional separators. -52.15 Mailbox maintenance (exim_lock) +53.15 Mailbox maintenance (exim_lock) ------------------------------------- The exim_lock utility locks a mailbox file using the same algorithm as Exim. @@ -33441,7 +34072,7 @@ =============================================================================== -53. THE EXIM MONITOR +54. THE EXIM MONITOR The Exim monitor is an application which displays in an X window information about the state of Exim's queue and what Exim is doing. An admin user can @@ -33450,7 +34081,7 @@ monitor itself makes use of the command line to perform any actions requested. -53.1 Running the monitor +54.1 Running the monitor ------------------------ The monitor is started by running the script called eximon. This is a shell @@ -33504,7 +34135,7 @@ different parts of the display. -53.2 The stripcharts +54.2 The stripcharts -------------------- The first stripchart is always a count of messages on the queue. Its name can @@ -33534,7 +34165,7 @@ file. -53.3 Main action buttons +54.3 Main action buttons ------------------------ Below the stripcharts there is an action button for quitting the monitor. Next @@ -33561,7 +34192,7 @@ START_SMALL=yes in Local/eximon.conf. -53.4 The log display +54.4 The log display -------------------- The second section of the window is an area in which a display of the tail of @@ -33613,7 +34244,7 @@ expense of having unwanted items in the search popup window. -53.5 The queue display +54.5 The queue display ---------------------- The bottom section of the monitor window contains a list of all messages that @@ -33664,7 +34295,7 @@ display is updated. -53.6 The queue menu +54.6 The queue menu ------------------- If the shift key is held down and the left button is clicked when the mouse @@ -33687,7 +34318,7 @@ in a new text window. * headers: Information from the spool file that contains the envelope - information and headers is displayed in a new text window. See chapter 55 + information and headers is displayed in a new text window. See chapter 56 for a description of the format of spool files. * body: The contents of the spool file containing the body of the message are @@ -33762,7 +34393,7 @@ =============================================================================== -54. SECURITY CONSIDERATIONS +55. SECURITY CONSIDERATIONS This chapter discusses a number of issues concerned with security, some of which are also covered in other parts of this manual. @@ -33780,7 +34411,7 @@ as soon as possible. -54.1 Building a more "hardened" Exim +55.1 Building a more "hardened" Exim ------------------------------------ There are a number of build-time options that can be set in Local/Makefile to @@ -33831,7 +34462,7 @@ to get root. -54.2 Root privilege +55.2 Root privilege ------------------- The Exim binary is normally setuid to root, which means that it gains root @@ -33916,7 +34547,7 @@ the routing is done in the same environment as a message delivery. -54.3 Running Exim without privilege +55.3 Running Exim without privilege ----------------------------------- Some installations like to run Exim in an unprivileged state for more of its @@ -33997,18 +34628,18 @@ gateway host that does no local deliveries, setting deliver_drop_privilege gives more security at essentially no cost. -If you are using the mua_wrapper facility (see chapter 50), +If you are using the mua_wrapper facility (see chapter 51), deliver_drop_privilege is forced to be true. -54.4 Delivering to local files +55.4 Delivering to local files ------------------------------ Full details of the checks applied by appendfile before it writes to a file are given in chapter 26. -54.5 Running local commands +55.5 Running local commands --------------------------- There are a number of ways in which an administrator can configure Exim to run @@ -34051,7 +34682,7 @@ the use of the inlisti expansion condition instead. -54.6 Trust in configuration data +55.6 Trust in configuration data -------------------------------- If configuration data for Exim can come from untrustworthy sources, there are @@ -34077,7 +34708,7 @@ only expected to yield one result. -54.7 IPv4 source routing +55.7 IPv4 source routing ------------------------ Many operating systems suppress IP source-routed packets in the kernel, but @@ -34086,14 +34717,14 @@ IPv6. No special checking is currently done. -54.8 The VRFY, EXPN, and ETRN commands in SMTP +55.8 The VRFY, EXPN, and ETRN commands in SMTP ---------------------------------------------- Support for these SMTP commands is disabled by default. If required, they can be enabled by defining suitable ACLs. -54.9 Privileged users +55.9 Privileged users --------------------- Exim recognizes two sets of users with special privileges. Trusted users are @@ -34137,7 +34768,7 @@ files. -54.10 Spool files +55.10 Spool files ----------------- Exim's spool directory and everything it contains is owned by the Exim user and @@ -34146,7 +34777,7 @@ is a member of the Exim group can access these files. -54.11 Use of argv[0] +55.11 Use of argv[0] -------------------- Exim examines the last component of argv[0], and if it matches one of a set of @@ -34155,7 +34786,7 @@ it with the option -bS. There are no security implications in this. -54.12 Use of %f formatting +55.12 Use of %f formatting -------------------------- The only use made of "%f" by Exim is in formatting load average values. These @@ -34164,7 +34795,7 @@ converted output. -54.13 Embedded Exim path +55.13 Embedded Exim path ------------------------ Exim uses its own path name, which is embedded in the code, only when it needs @@ -34173,14 +34804,14 @@ arbitrary program's being run as exim, not as root. -54.14 Dynamic module directory +55.14 Dynamic module directory ------------------------------ Any dynamically loadable modules must be installed into the directory defined in "LOOKUP_MODULE_DIR" in Local/Makefile for Exim to permit loading it. -54.15 Use of sprintf() +55.15 Use of sprintf() ---------------------- A large number of occurrences of "sprintf" in the code are actually calls to @@ -34193,7 +34824,7 @@ output buffer is known to be sufficiently long to contain the converted string. -54.16 Use of debug_printf() and log_write() +55.16 Use of debug_printf() and log_write() ------------------------------------------- Arbitrary strings are passed to both these functions, but they do their @@ -34201,7 +34832,7 @@ format string itself, and checks the length of each conversion. -54.17 Use of strcat() and strcpy() +55.17 Use of strcat() and strcpy() ---------------------------------- These are used only in cases where the output buffer is known to be large @@ -34210,7 +34841,7 @@ =============================================================================== -55. FORMAT OF SPOOL FILES +56. FORMAT OF SPOOL FILES A message on Exim's queue consists of two files, whose names are the message id followed by -D and -H, respectively. The data portion of the message is kept in @@ -34253,7 +34884,7 @@ attempt. -55.1 Format of the -H file +56.1 Format of the -H file -------------------------- The second line of the -H file contains the login name for the uid of the @@ -34541,7 +35172,7 @@ =============================================================================== -56. SUPPORT FOR DKIM (DOMAINKEYS IDENTIFIED MAIL) +57. SUPPORT FOR DKIM (DOMAINKEYS IDENTIFIED MAIL) DKIM is a mechanism by which messages sent by some entity can be provably linked to a domain which that entity controls. It permits reputation to be @@ -34581,31 +35212,31 @@ accept mail from relay sources (internal hosts or authenticated senders). -56.1 Signing outgoing messages +57.1 Signing outgoing messages ------------------------------ Signing is implemented by setting private options on the SMTP transport. These options take (expandable) strings as arguments. -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ |dkim_domain|Use: smtp|Type: string*|Default: unset| -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ MANDATORY: The domain you want to sign with. The result of this expanded option is put into the $dkim_domain expansion variable. -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ |dkim_selector|Use: smtp|Type: string*|Default: unset| -+-------------+---------+-------------+--------------+ ++----------------------------------------------------+ MANDATORY: This sets the key selector string. You can use the $dkim_domain expansion variable to look up a matching selector. The result is put in the expansion variable $dkim_selector which should be used in the dkim_private_key option along with $dkim_domain. -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ |dkim_private_key|Use: smtp|Type: string*|Default: unset| -+----------------+---------+-------------+--------------+ ++-------------------------------------------------------+ MANDATORY: This sets the private key to use. You can use the $dkim_domain and $dkim_selector expansion variables to determine the private key to use. The @@ -34619,27 +35250,27 @@ * be "0", "false" or the empty string, in which case the message will not be signed. This case will not result in an error, even if dkim_strict is set. -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ |dkim_canon|Use: smtp|Type: string*|Default: unset| -+----------+---------+-------------+--------------+ ++-------------------------------------------------+ OPTIONAL: This option sets the canonicalization method used when signing a message. The DKIM RFC currently supports two methods: "simple" and "relaxed". The option defaults to "relaxed" when unset. Note: the current implementation only supports using the same canonicalization method for both headers and body. -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ |dkim_strict|Use: smtp|Type: string*|Default: unset| -+-----------+---------+-------------+--------------+ ++--------------------------------------------------+ OPTIONAL: This option defines how Exim behaves when signing a message that should be signed fails for some reason. When the expansion evaluates to either "1" or "true", Exim will defer. Otherwise Exim will send the message unsigned. You can use the $dkim_domain and $dkim_selector expansion variables here. -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ |dkim_sign_headers|Use: smtp|Type: string*|Default: unset| -+-----------------+---------+-------------+--------------+ ++--------------------------------------------------------+ OPTIONAL: When set, this option must expand to (or be specified as) a colon-separated list of header names. Headers with these names will be included @@ -34647,7 +35278,7 @@ RFC4871 will be used. -56.2 Verifying DKIM signatures in incoming mail +57.2 Verifying DKIM signatures in incoming mail ----------------------------------------------- Verification of DKIM signatures in incoming email is implemented via the @@ -34846,7 +35477,7 @@ =============================================================================== -57. ADDING NEW DRIVERS OR LOOKUP TYPES +58. ADDING NEW DRIVERS OR LOOKUP TYPES The following actions have to be taken in order to add a new router, transport, authenticator, or lookup type to Exim: diff -Nru exim4-4.84/exim_monitor/em_globals.c exim4-4.86~RC4/exim_monitor/em_globals.c --- exim4-4.84/exim_monitor/em_globals.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/exim_monitor/em_globals.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim Monitor * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -145,10 +145,8 @@ BOOL dont_deliver = FALSE; -#ifdef EXPERIMENTAL_DSN int dsn_ret = 0; uschar *dsn_envid = NULL; -#endif #ifdef WITH_CONTENT_SCAN int fake_response = OK; @@ -220,6 +218,10 @@ -1, /* tls_active */ 0, /* bits */ FALSE, /* tls_certificate_verified */ +#ifdef EXPERIMENTAL_DANE + FALSE, /* dane_verified */ + 0, /* tlsa_usage */ +#endif NULL, /* tls_cipher */ FALSE, /* tls_on_connect */ NULL, /* tls_on_connect_ports */ diff -Nru exim4-4.84/exim_monitor/em_main.c exim4-4.86~RC4/exim_monitor/em_main.c --- exim4-4.84/exim_monitor/em_main.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/exim_monitor/em_main.c 2015-06-27 17:01:28.000000000 +0200 @@ -613,7 +613,6 @@ constructing file names and things. This call will initialize the store_get() function. */ -big_buffer_size = 1024; big_buffer = store_get(big_buffer_size); /* Set up the version string and date and output them */ diff -Nru exim4-4.84/.gitignore exim4-4.86~RC4/.gitignore --- exim4-4.84/.gitignore 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/.gitignore 2015-06-27 17:01:28.000000000 +0200 @@ -1,4 +1,4 @@ Local build-* tags -cscope.out +cscope.* diff -Nru exim4-4.84/Makefile exim4-4.86~RC4/Makefile --- exim4-4.84/Makefile 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/Makefile 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ # appropriate links, and then creating and running the main makefile in that # directory. -# Copyright (c) University of Cambridge, 1995 - 2014 +# Copyright (c) University of Cambridge, 1995 - 2015 # See the file NOTICE for conditions of use and distribution. # IRIX make uses the shell that is in the SHELL variable, which often defaults @@ -28,6 +28,14 @@ all: Local/Makefile configure @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) + +# This pair for the convinience of of the Debian maintainers +exim: Local/Makefile configure + @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) exim +utils: Local/Makefile configure + @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) utils + + Local/Makefile: @echo "" @echo "*** Please create Local/Makefile by copying src/EDITME and making" @@ -90,9 +98,10 @@ cscope.files: FRC echo "-q" > $@ echo "-p3" >> $@ - find src Local OS -name "*.[cshyl]" -print \ + find src Local OS exim_monitor -name "*.[cshyl]" -print \ -o -name "os.h*" -print \ -o -name "*akefile*" -print \ + -o -name config.h.defaults -print \ -o -name EDITME -print >> $@ ls OS/* >> $@ diff -Nru exim4-4.84/OS/Makefile-Base exim4-4.86~RC4/OS/Makefile-Base --- exim4-4.84/OS/Makefile-Base 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/Makefile-Base 2015-06-27 17:01:28.000000000 +0200 @@ -32,10 +32,10 @@ # up-to-date. Then the os-specific source files and the C configuration file # are set up, and finally it goes to the main Exim target. -all: allexim -config: $(EDITME) checklocalmake Makefile os.h os.c config.h version.h +all: utils exim +config: $(EDITME) checklocalmake Makefile os.c config.h version.h -checklocalmake: +checklocalmake: @if $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(OSTYPE) $(EDITME) || \ $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(ARCHTYPE) $(EDITME) || \ $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(OSTYPE)-$(ARCHTYPE) $(EDITME); \ @@ -95,19 +95,16 @@ # therefore always be run, even if the files exist. This shouldn't in fact be a # problem, but it does no harm. Other make programs will just ignore this. -.PHONY: all config allexim buildauths buildlookups buildpdkim buildrouters \ +.PHONY: all config utils \ + buildauths buildlookups buildpdkim buildrouters \ buildtransports checklocalmake clean -# This is the real default target for all the various exim binaries and -# scripts, once the configuring stuff is done. - -allexim: $(EXIM_MONITOR) exicyclog exinext exiwhat \ +utils: $(EXIM_MONITOR) exicyclog exinext exiwhat \ exigrep eximstats exipick exiqgrep exiqsumm \ transport-filter.pl convert4r3 convert4r4 \ exim_checkaccess \ - exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock \ - exim + exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock # Targets for special-purpose configuration header builders @@ -298,7 +295,14 @@ OBJ_WITH_CONTENT_SCAN = malware.o mime.o regex.o spam.o spool_mbox.o OBJ_WITH_OLD_DEMIME = demime.o -OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dcc.o dmarc.o +OBJ_EXPERIMENTAL = bmi_spam.o \ + dane.o \ + dcc.o \ + dmarc.o \ + imap_utf7.o \ + spf.o \ + srs.o \ + utf8.o # Targets for final binaries; the main one has a build number which is # updated each time. We don't bother with that for the auxiliaries. @@ -396,7 +400,7 @@ # The utility for locking a mailbox while messing around with it -exim_lock: exim_lock.c +exim_lock: exim_lock.c os.h @echo "$(CC) exim_lock.c" $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) exim_lock.c @echo "$(LNCC) -o exim_lock" @@ -448,8 +452,28 @@ # in one. This list is overkill, but it doesn't really take much time to # rebuild Exim on a modern computer. -HDRS = config.h dbfunctions.h dbstuff.h exim.h functions.h globals.h local_scan.h macros.h mytypes.h structs.h -PHDRS = ../config.h ../dbfunctions.h ../dbstuff.h ../exim.h ../functions.h ../globals.h ../local_scan.h ../macros.h ../mytypes.h ../structs.h +HDRS = config.h \ + dbfunctions.h \ + dbstuff.h \ + exim.h \ + functions.h \ + globals.h \ + local_scan.h \ + macros.h \ + mytypes.h \ + structs.h \ + os.h +PHDRS = ../config.h \ + ../dbfunctions.h \ + ../dbstuff.h \ + ../exim.h \ + ../functions.h \ + ../globals.h \ + ../local_scan.h \ + ../macros.h \ + ../mytypes.h \ + ../structs.h \ + ../os.h .SUFFIXES: .o .c .c.o:; @echo "$(CC) $*.c" @@ -601,11 +625,14 @@ # Dependencies for EXPERIMENTAL_* modules -bmi_spam.o: $(HDRS) bmi_spam.c -spf.o: $(HDRS) spf.h spf.c -srs.o: $(HDRS) srs.h srs.c -dcc.o: $(HDRS) dcc.h dcc.c -dmarc.o: $(HDRS) dmarc.h dmarc.c +bmi_spam.o: $(HDRS) bmi_spam.c +dane.o: $(HDRS) dane.c dane-gnu.c dane-openssl.c +dcc.o: $(HDRS) dcc.h dcc.c +dmarc.o: $(HDRS) dmarc.h dmarc.c +imap_utf7.o: $(HDRS) imap_utf7.c +spf.o: $(HDRS) spf.h spf.c +srs.o: $(HDRS) srs.h srs.c +utf8.o: $(HDRS) utf8.c # The module containing tables of available lookups, routers, auths, and # transports must be rebuilt if any of them are. However, because the makefiles diff -Nru exim4-4.84/OS/Makefile-Default exim4-4.86~RC4/OS/Makefile-Default --- exim4-4.84/OS/Makefile-Default 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/Makefile-Default 2015-06-27 17:01:28.000000000 +0200 @@ -186,14 +186,6 @@ # IPV6_USE_INET_PTON=yes -# Setting the next option brings in support for A6 DNS records for IPV6. These -# were at one time expected to supplant AAAA records, but were eventually -# rejected. The code remains in Exim, but has not been compiled or tested for -# quite some time. Do not set this unless you know what you are doing. - -# SUPPORT_A6=yes - - # HOSTNAME_COMMAND contains the path to the "hostname" command, which varies # from OS to OS. This is used when building the Exim monitor script only. (See # also BASENAME_COMMAND.) If HOSTNAME_COMMAND is set to "look_for_it" then the diff -Nru exim4-4.84/OS/os.h-AIX exim4-4.86~RC4/OS/os.h-AIX --- exim4-4.84/OS/os.h-AIX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-AIX 2015-06-27 17:01:28.000000000 +0200 @@ -20,4 +20,8 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + + /* End */ diff -Nru exim4-4.84/OS/os.h-BSDI exim4-4.86~RC4/OS/os.h-BSDI --- exim4-4.84/OS/os.h-BSDI 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-BSDI 2015-06-27 17:01:28.000000000 +0200 @@ -8,4 +8,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-cygwin exim4-4.86~RC4/OS/os.h-cygwin --- exim4-4.84/OS/os.h-cygwin 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-cygwin 2015-06-27 17:01:28.000000000 +0200 @@ -48,4 +48,7 @@ DWORD SubAuthority[n]; \ } name = { SID_REVISION, n, {SECURITY_NT_AUTHORITY}, {sid}} +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-Darwin exim4-4.86~RC4/OS/os.h-Darwin --- exim4-4.84/OS/os.h-Darwin 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-Darwin 2015-06-27 17:01:28.000000000 +0200 @@ -42,4 +42,7 @@ #define OFF_T_FMT "%lld" #define LONGLONG_T long int +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-DGUX exim4-4.86~RC4/OS/os.h-DGUX --- exim4-4.84/OS/os.h-DGUX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-DGUX 2015-06-27 17:01:28.000000000 +0200 @@ -22,4 +22,7 @@ #define NO_IP_OPTIONS +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-DragonFly exim4-4.86~RC4/OS/os.h-DragonFly --- exim4-4.84/OS/os.h-DragonFly 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-DragonFly 2015-06-27 17:01:28.000000000 +0200 @@ -7,4 +7,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-FreeBSD exim4-4.86~RC4/OS/os.h-FreeBSD --- exim4-4.84/OS/os.h-FreeBSD 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-FreeBSD 2015-06-27 17:01:28.000000000 +0200 @@ -10,4 +10,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-GNU exim4-4.86~RC4/OS/os.h-GNU --- exim4-4.84/OS/os.h-GNU 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-GNU 2015-06-27 17:01:28.000000000 +0200 @@ -17,4 +17,7 @@ /* Hurd-specific bits below */ +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-GNUkFreeBSD exim4-4.86~RC4/OS/os.h-GNUkFreeBSD --- exim4-4.84/OS/os.h-GNUkFreeBSD 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-GNUkFreeBSD 2015-06-27 17:01:28.000000000 +0200 @@ -19,4 +19,7 @@ #define HAVE_SYS_MOUNT_H #define SIOCGIFCONF_GIVES_ADDR +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-GNUkNetBSD exim4-4.86~RC4/OS/os.h-GNUkNetBSD --- exim4-4.84/OS/os.h-GNUkNetBSD 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-GNUkNetBSD 2015-06-27 17:01:28.000000000 +0200 @@ -19,4 +19,7 @@ #define HAVE_SYS_MOUNT_H #define SIOCGIFCONF_GIVES_ADDR +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-HI-OSF exim4-4.86~RC4/OS/os.h-HI-OSF --- exim4-4.84/OS/os.h-HI-OSF 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-HI-OSF 2015-06-27 17:01:28.000000000 +0200 @@ -6,4 +6,7 @@ #define F_FREESP O_TRUNC #define DN_EXPAND_ARG4_TYPE u_char * +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-HI-UX exim4-4.86~RC4/OS/os.h-HI-UX --- exim4-4.84/OS/os.h-HI-UX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-HI-UX 2015-06-27 17:01:28.000000000 +0200 @@ -15,4 +15,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-HP-UX exim4-4.86~RC4/OS/os.h-HP-UX --- exim4-4.84/OS/os.h-HP-UX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-HP-UX 2015-06-27 17:01:28.000000000 +0200 @@ -1,6 +1,5 @@ /* Exim: OS-specific C header file for HP-UX versions greater than 9 */ -#define ICONV_ARG2_TYPE char ** #define EXIM_SOCKLEN_T size_t #define LOAD_AVG_NEEDS_ROOT diff -Nru exim4-4.84/OS/os.h-HP-UX-9 exim4-4.86~RC4/OS/os.h-HP-UX-9 --- exim4-4.84/OS/os.h-HP-UX-9 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-HP-UX-9 2015-06-27 17:01:28.000000000 +0200 @@ -17,4 +17,7 @@ typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-IRIX exim4-4.86~RC4/OS/os.h-IRIX --- exim4-4.84/OS/os.h-IRIX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-IRIX 2015-06-27 17:01:28.000000000 +0200 @@ -14,7 +14,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff -Nru exim4-4.84/OS/os.h-IRIX6 exim4-4.86~RC4/OS/os.h-IRIX6 --- exim4-4.84/OS/os.h-IRIX6 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-IRIX6 2015-06-27 17:01:28.000000000 +0200 @@ -13,7 +13,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff -Nru exim4-4.84/OS/os.h-IRIX632 exim4-4.86~RC4/OS/os.h-IRIX632 --- exim4-4.84/OS/os.h-IRIX632 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-IRIX632 2015-06-27 17:01:28.000000000 +0200 @@ -15,7 +15,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff -Nru exim4-4.84/OS/os.h-IRIX65 exim4-4.86~RC4/OS/os.h-IRIX65 --- exim4-4.84/OS/os.h-IRIX65 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-IRIX65 2015-06-27 17:01:28.000000000 +0200 @@ -13,7 +13,4 @@ #define F_FAVAIL f_favail #define vfork fork -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff -Nru exim4-4.84/OS/os.h-Linux exim4-4.86~RC4/OS/os.h-Linux --- exim4-4.84/OS/os.h-Linux 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-Linux 2015-06-27 17:01:28.000000000 +0200 @@ -44,9 +44,6 @@ #define NEED_SYNC_DIRECTORY -/* Other OS have "const" in here */ -#define ICONV_ARG2_TYPE char ** - #define os_find_running_interfaces os_find_running_interfaces_linux /* Need a prototype for the Linux-specific function. The structure hasn't diff -Nru exim4-4.84/OS/os.h-mips exim4-4.86~RC4/OS/os.h-mips --- exim4-4.84/OS/os.h-mips 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-mips 2015-06-27 17:01:28.000000000 +0200 @@ -21,4 +21,7 @@ extern int sys_nerr; extern char *sys_errlist[]; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-NetBSD exim4-4.86~RC4/OS/os.h-NetBSD --- exim4-4.84/OS/os.h-NetBSD 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-NetBSD 2015-06-27 17:01:28.000000000 +0200 @@ -22,4 +22,7 @@ #define HAVE_SYS_STATVFS_H #endif +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-OpenBSD exim4-4.86~RC4/OS/os.h-OpenBSD --- exim4-4.84/OS/os.h-OpenBSD 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-OpenBSD 2015-06-27 17:01:28.000000000 +0200 @@ -5,6 +5,13 @@ #define HAVE_SYS_MOUNT_H #define SIOCGIFCONF_GIVES_ADDR #define HAVE_ARC4RANDOM +/* In May 2014, OpenBSD 5.5 was released which cleaned up the arc4random_* API + which removed the arc4random_stir() function. Set NOT_HAVE_ARC4RANDOM_STIR + if the version released is past that point. */ +#include +#if OpenBSD >= 201405 +#define NOT_HAVE_ARC4RANDOM_STIR +#endif typedef struct flock flock_t; @@ -13,4 +20,7 @@ typedef struct __res_state *res_state; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-OpenUNIX exim4-4.86~RC4/OS/os.h-OpenUNIX --- exim4-4.84/OS/os.h-OpenUNIX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-OpenUNIX 2015-06-27 17:01:28.000000000 +0200 @@ -13,4 +13,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-OSF1 exim4-4.86~RC4/OS/os.h-OSF1 --- exim4-4.84/OS/os.h-OSF1 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-OSF1 2015-06-27 17:01:28.000000000 +0200 @@ -13,7 +13,4 @@ /* Still not "socklen_t", which is the most common setting */ #define EXIM_SOCKLEN_T int -/* The default for this is "const char **" */ -#define ICONV_ARG2_TYPE char ** - /* End */ diff -Nru exim4-4.84/OS/os.h-QNX exim4-4.86~RC4/OS/os.h-QNX --- exim4-4.84/OS/os.h-QNX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-QNX 2015-06-27 17:01:28.000000000 +0200 @@ -18,4 +18,7 @@ extern int h_errno; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-SCO exim4-4.86~RC4/OS/os.h-SCO --- exim4-4.84/OS/os.h-SCO 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-SCO 2015-06-27 17:01:28.000000000 +0200 @@ -15,4 +15,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-SCO_SV exim4-4.86~RC4/OS/os.h-SCO_SV --- exim4-4.84/OS/os.h-SCO_SV 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-SCO_SV 2015-06-27 17:01:28.000000000 +0200 @@ -13,4 +13,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-SunOS4 exim4-4.86~RC4/OS/os.h-SunOS4 --- exim4-4.84/OS/os.h-SunOS4 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-SunOS4 2015-06-27 17:01:28.000000000 +0200 @@ -33,4 +33,7 @@ #define FUDGE_GETC_AND_FRIENDS +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-SunOS5 exim4-4.86~RC4/OS/os.h-SunOS5 --- exim4-4.84/OS/os.h-SunOS5 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-SunOS5 2015-06-27 17:01:28.000000000 +0200 @@ -28,4 +28,7 @@ #define PAM_CONVERSE_ARG2_TYPE struct pam_message +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-SunOS5-hal exim4-4.86~RC4/OS/os.h-SunOS5-hal --- exim4-4.84/OS/os.h-SunOS5-hal 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-SunOS5-hal 2015-06-27 17:01:28.000000000 +0200 @@ -8,4 +8,7 @@ #define LOAD_AVG_SYMBOL "avenrun_1min" #define LOAD_AVG_FIELD value.ul +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-ULTRIX exim4-4.86~RC4/OS/os.h-ULTRIX --- exim4-4.84/OS/os.h-ULTRIX 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-ULTRIX 2015-06-27 17:01:28.000000000 +0200 @@ -12,4 +12,7 @@ #define NO_OPENLOG typedef struct flock flock_t; +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-UNIX_SV exim4-4.86~RC4/OS/os.h-UNIX_SV --- exim4-4.84/OS/os.h-UNIX_SV 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-UNIX_SV 2015-06-27 17:01:28.000000000 +0200 @@ -19,4 +19,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/OS/os.h-Unixware7 exim4-4.86~RC4/OS/os.h-Unixware7 --- exim4-4.84/OS/os.h-Unixware7 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-Unixware7 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,6 @@ #define NO_SYSEXITS -#define ICONV_ARG2_TYPE char ** #define EXIM_SOCKLEN_T size_t #define LOAD_AVG_NEEDS_ROOT diff -Nru exim4-4.84/OS/os.h-USG exim4-4.86~RC4/OS/os.h-USG --- exim4-4.84/OS/os.h-USG 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/OS/os.h-USG 2015-06-27 17:01:28.000000000 +0200 @@ -13,4 +13,7 @@ #define _SVID3 #define NEED_H_ERRNO +/* default is non-const */ +#define ICONV_ARG2_TYPE const char ** + /* End */ diff -Nru exim4-4.84/scripts/MakeLinks exim4-4.86~RC4/scripts/MakeLinks --- exim4-4.84/scripts/MakeLinks 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/scripts/MakeLinks 2015-06-27 17:01:28.000000000 +0200 @@ -27,250 +27,106 @@ # Firstly the lookups mkdir lookups cd lookups -ln -s ../../src/lookups/README README # Makefile is generated -ln -s ../../src/lookups/cdb.c cdb.c -ln -s ../../src/lookups/dbmdb.c dbmdb.c -ln -s ../../src/lookups/dnsdb.c dnsdb.c -ln -s ../../src/lookups/dsearch.c dsearch.c -ln -s ../../src/lookups/ibase.c ibase.c -ln -s ../../src/lookups/ldap.h ldap.h -ln -s ../../src/lookups/ldap.c ldap.c -ln -s ../../src/lookups/lsearch.c lsearch.c -ln -s ../../src/lookups/mysql.c mysql.c -ln -s ../../src/lookups/redis.c redis.c -ln -s ../../src/lookups/nis.c nis.c -ln -s ../../src/lookups/nisplus.c nisplus.c -ln -s ../../src/lookups/oracle.c oracle.c -ln -s ../../src/lookups/passwd.c passwd.c -ln -s ../../src/lookups/pgsql.c pgsql.c -ln -s ../../src/lookups/spf.c spf.c -ln -s ../../src/lookups/sqlite.c sqlite.c -ln -s ../../src/lookups/testdb.c testdb.c -ln -s ../../src/lookups/whoson.c whoson.c - -ln -s ../../src/lookups/lf_functions.h lf_functions.h -ln -s ../../src/lookups/lf_check_file.c lf_check_file.c -ln -s ../../src/lookups/lf_quote.c lf_quote.c -ln -s ../../src/lookups/lf_sqlperform.c lf_sqlperform.c +for f in README cdb.c dbmdb.c dnsdb.c dsearch.c ibase.c ldap.h ldap.c \ + lsearch.c mysql.c redis.c nis.c nisplus.c oracle.c passwd.c \ + pgsql.c spf.c sqlite.c testdb.c whoson.c \ + lf_functions.h lf_check_file.c lf_quote.c lf_sqlperform.c +do + ln -s ../../src/lookups/$f $f +done cd .. # Likewise for the code for the routers mkdir routers cd routers -ln -s ../../src/routers/README README -ln -s ../../src/routers/Makefile Makefile -ln -s ../../src/routers/accept.h accept.h -ln -s ../../src/routers/accept.c accept.c -ln -s ../../src/routers/dnslookup.h dnslookup.h -ln -s ../../src/routers/dnslookup.c dnslookup.c -ln -s ../../src/routers/ipliteral.h ipliteral.h -ln -s ../../src/routers/ipliteral.c ipliteral.c -ln -s ../../src/routers/iplookup.h iplookup.h -ln -s ../../src/routers/iplookup.c iplookup.c -ln -s ../../src/routers/manualroute.h manualroute.h -ln -s ../../src/routers/manualroute.c manualroute.c -ln -s ../../src/routers/queryprogram.h queryprogram.h -ln -s ../../src/routers/queryprogram.c queryprogram.c -ln -s ../../src/routers/redirect.h redirect.h -ln -s ../../src/routers/redirect.c redirect.c - -ln -s ../../src/routers/rf_functions.h rf_functions.h -ln -s ../../src/routers/rf_change_domain.c rf_change_domain.c -ln -s ../../src/routers/rf_expand_data.c rf_expand_data.c -ln -s ../../src/routers/rf_get_errors_address.c rf_get_errors_address.c -ln -s ../../src/routers/rf_get_munge_headers.c rf_get_munge_headers.c -ln -s ../../src/routers/rf_get_transport.c rf_get_transport.c -ln -s ../../src/routers/rf_get_ugid.c rf_get_ugid.c -ln -s ../../src/routers/rf_queue_add.c rf_queue_add.c -ln -s ../../src/routers/rf_lookup_hostlist.c rf_lookup_hostlist.c -ln -s ../../src/routers/rf_self_action.c rf_self_action.c -ln -s ../../src/routers/rf_set_ugid.c rf_set_ugid.c +for f in README Makefile accept.h accept.c dnslookup.h dnslookup.c \ + ipliteral.h ipliteral.c iplookup.h iplookup.c manualroute.h \ + manualroute.c queryprogram.h queryprogram.c redirect.h redirect.c \ + rf_functions.h rf_change_domain.c rf_expand_data.c rf_get_errors_address.c \ + rf_get_munge_headers.c rf_get_transport.c rf_get_ugid.c rf_queue_add.c \ + rf_lookup_hostlist.c rf_self_action.c rf_set_ugid.c +do + ln -s ../../src/routers/$f $f +done cd .. # Likewise for the code for the transports mkdir transports cd transports -ln -s ../../src/transports/README README -ln -s ../../src/transports/Makefile Makefile -ln -s ../../src/transports/appendfile.h appendfile.h -ln -s ../../src/transports/appendfile.c appendfile.c -ln -s ../../src/transports/autoreply.h autoreply.h -ln -s ../../src/transports/autoreply.c autoreply.c -ln -s ../../src/transports/lmtp.h lmtp.h -ln -s ../../src/transports/lmtp.c lmtp.c -ln -s ../../src/transports/pipe.h pipe.h -ln -s ../../src/transports/pipe.c pipe.c -ln -s ../../src/transports/smtp.h smtp.h -ln -s ../../src/transports/smtp.c smtp.c - -ln -s ../../src/transports/tf_maildir.c tf_maildir.c -ln -s ../../src/transports/tf_maildir.h tf_maildir.h +for f in README Makefile appendfile.h appendfile.c autoreply.h \ + autoreply.c lmtp.h lmtp.c pipe.h pipe.c smtp.h smtp.c smtp_socks.c \ + tf_maildir.c tf_maildir.h +do + ln -s ../../src/transports/$f $f +done cd .. # Likewise for the code for the authorization functions mkdir auths cd auths -ln -s ../../src/auths/README README -ln -s ../../src/auths/Makefile Makefile -ln -s ../../src/auths/b64encode.c b64encode.c -ln -s ../../src/auths/b64decode.c b64decode.c -ln -s ../../src/auths/call_pam.c call_pam.c -ln -s ../../src/auths/call_pwcheck.c call_pwcheck.c -ln -s ../../src/auths/call_radius.c call_radius.c -ln -s ../../src/auths/check_serv_cond.c check_serv_cond.c -ln -s ../../src/auths/cyrus_sasl.c cyrus_sasl.c -ln -s ../../src/auths/cyrus_sasl.h cyrus_sasl.h -ln -s ../../src/auths/gsasl_exim.c gsasl_exim.c -ln -s ../../src/auths/gsasl_exim.h gsasl_exim.h -ln -s ../../src/auths/get_data.c get_data.c -ln -s ../../src/auths/get_no64_data.c get_no64_data.c -ln -s ../../src/auths/heimdal_gssapi.c heimdal_gssapi.c -ln -s ../../src/auths/heimdal_gssapi.h heimdal_gssapi.h -ln -s ../../src/auths/md5.c md5.c -ln -s ../../src/auths/xtextencode.c xtextencode.c -ln -s ../../src/auths/xtextdecode.c xtextdecode.c -ln -s ../../src/auths/cram_md5.c cram_md5.c -ln -s ../../src/auths/cram_md5.h cram_md5.h -ln -s ../../src/auths/plaintext.c plaintext.c -ln -s ../../src/auths/plaintext.h plaintext.h -ln -s ../../src/auths/pwcheck.c pwcheck.c -ln -s ../../src/auths/pwcheck.h pwcheck.h -ln -s ../../src/auths/auth-spa.c auth-spa.c -ln -s ../../src/auths/auth-spa.h auth-spa.h -ln -s ../../src/auths/dovecot.c dovecot.c -ln -s ../../src/auths/dovecot.h dovecot.h -ln -s ../../src/auths/sha1.c sha1.c -ln -s ../../src/auths/spa.c spa.c -ln -s ../../src/auths/spa.h spa.h +for f in README Makefile b64encode.c b64decode.c call_pam.c call_pwcheck.c \ + call_radius.c check_serv_cond.c cyrus_sasl.c cyrus_sasl.h gsasl_exim.c \ + gsasl_exim.h get_data.c get_no64_data.c heimdal_gssapi.c heimdal_gssapi.h \ + md5.c xtextencode.c xtextdecode.c cram_md5.c cram_md5.h plaintext.c plaintext.h \ + pwcheck.c pwcheck.h auth-spa.c auth-spa.h dovecot.c dovecot.h sha1.c spa.c \ + spa.h tls.c tls.h +do + ln -s ../../src/auths/$f $f +done cd .. # Likewise for the code for the PDKIM library mkdir pdkim cd pdkim -ln -s ../../src/pdkim/README README -ln -s ../../src/pdkim/Makefile Makefile -ln -s ../../src/pdkim/base64.c base64.c -ln -s ../../src/pdkim/base64.h base64.h -ln -s ../../src/pdkim/bignum.c bignum.c -ln -s ../../src/pdkim/bignum.h bignum.h -ln -s ../../src/pdkim/bn_mul.h bn_mul.h -ln -s ../../src/pdkim/pdkim.c pdkim.c -ln -s ../../src/pdkim/pdkim.h pdkim.h -ln -s ../../src/pdkim/rsa.c rsa.c -ln -s ../../src/pdkim/rsa.h rsa.h -ln -s ../../src/pdkim/sha1.c sha1.c -ln -s ../../src/pdkim/sha1.h sha1.h -ln -s ../../src/pdkim/sha2.c sha2.c -ln -s ../../src/pdkim/sha2.h sha2.h +for f in README Makefile base64.c base64.h bignum.c bignum.h bn_mul.h pdkim.c \ + pdkim.h rsa.c rsa.h sha1.c sha1.h sha2.c sha2.h +do + ln -s ../../src/pdkim/$f $f +done cd .. # The basic source files for Exim and utilities. NB local_scan.h gets linked, # but local_scan.c does not, because its location is taken from the build-time # configuration. Likewise for the os.c file, which gets build dynamically. -ln -s ../src/dbfunctions.h dbfunctions.h -ln -s ../src/dbstuff.h dbstuff.h -ln -s ../src/exim.h exim.h -ln -s ../src/functions.h functions.h -ln -s ../src/globals.h globals.h -ln -s ../src/local_scan.h local_scan.h -ln -s ../src/macros.h macros.h -ln -s ../src/mytypes.h mytypes.h -ln -s ../src/osfunctions.h osfunctions.h -ln -s ../src/store.h store.h -ln -s ../src/structs.h structs.h -ln -s ../src/lookupapi.h lookupapi.h - -ln -s ../src/acl.c acl.c -ln -s ../src/buildconfig.c buildconfig.c -ln -s ../src/child.c child.c -ln -s ../src/crypt16.c crypt16.c -ln -s ../src/daemon.c daemon.c -ln -s ../src/dbfn.c dbfn.c -ln -s ../src/debug.c debug.c -ln -s ../src/deliver.c deliver.c -ln -s ../src/directory.c directory.c -ln -s ../src/dns.c dns.c -ln -s ../src/drtables.c drtables.c -ln -s ../src/dummies.c dummies.c -ln -s ../src/enq.c enq.c -ln -s ../src/exim.c exim.c -ln -s ../src/exim_dbmbuild.c exim_dbmbuild.c -ln -s ../src/exim_dbutil.c exim_dbutil.c -ln -s ../src/exim_lock.c exim_lock.c -ln -s ../src/expand.c expand.c -ln -s ../src/filter.c filter.c -ln -s ../src/filtertest.c filtertest.c -ln -s ../src/globals.c globals.c -ln -s ../src/header.c header.c -ln -s ../src/host.c host.c -ln -s ../src/ip.c ip.c -ln -s ../src/log.c log.c -ln -s ../src/lss.c lss.c -ln -s ../src/match.c match.c -ln -s ../src/moan.c moan.c -ln -s ../src/parse.c parse.c -ln -s ../src/perl.c perl.c -ln -s ../src/queue.c queue.c -ln -s ../src/rda.c rda.c -ln -s ../src/readconf.c readconf.c -ln -s ../src/receive.c receive.c -ln -s ../src/retry.c retry.c -ln -s ../src/rewrite.c rewrite.c -ln -s ../src/rfc2047.c rfc2047.c -ln -s ../src/route.c route.c -ln -s ../src/search.c search.c -ln -s ../src/sieve.c sieve.c -ln -s ../src/smtp_in.c smtp_in.c -ln -s ../src/smtp_out.c smtp_out.c -ln -s ../src/spool_in.c spool_in.c -ln -s ../src/spool_out.c spool_out.c -ln -s ../src/std-crypto.c std-crypto.c -ln -s ../src/store.c store.c -ln -s ../src/string.c string.c -ln -s ../src/tls.c tls.c -ln -s ../src/tlscert-gnu.c tlscert-gnu.c -ln -s ../src/tlscert-openssl.c tlscert-openssl.c -ln -s ../src/tls-gnu.c tls-gnu.c -ln -s ../src/tls-openssl.c tls-openssl.c -ln -s ../src/tod.c tod.c -ln -s ../src/transport.c transport.c -ln -s ../src/tree.c tree.c -ln -s ../src/verify.c verify.c -ln -s ../src/version.c version.c -ln -s ../src/dkim.c dkim.c -ln -s ../src/dkim.h dkim.h -ln -s ../src/dmarc.c dmarc.c -ln -s ../src/dmarc.h dmarc.h -ln -s ../src/valgrind.h valgrind.h -ln -s ../src/memcheck.h memcheck.h +for f in dbfunctions.h dbstuff.h exim.h functions.h globals.h local_scan.h \ + macros.h mytypes.h osfunctions.h store.h structs.h lookupapi.h \ + \ + acl.c buildconfig.c child.c crypt16.c daemon.c dbfn.c debug.c deliver.c \ + directory.c dns.c drtables.c dummies.c enq.c exim.c exim_dbmbuild.c \ + exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c globals.c \ + header.c host.c ip.c log.c lss.c match.c moan.c parse.c perl.c queue.c \ + rda.c readconf.c receive.c retry.c rewrite.c rfc2047.c route.c search.c \ + sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \ + string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-gnu.c tls-openssl.c \ + tod.c transport.c tree.c verify.c version.c dkim.c dkim.h dmarc.c dmarc.h \ + valgrind.h memcheck.h +do + ln -s ../src/$f $f +done # WITH_CONTENT_SCAN -ln -s ../src/spam.c spam.c -ln -s ../src/spam.h spam.h -ln -s ../src/spool_mbox.c spool_mbox.c -ln -s ../src/regex.c regex.c -ln -s ../src/mime.c mime.c -ln -s ../src/mime.h mime.h -ln -s ../src/malware.c malware.c +for f in spam.c spam.h spool_mbox.c regex.c mime.c mime.h malware.c +do + ln -s ../src/$f $f +done # WITH_OLD_DEMIME -ln -s ../src/demime.c demime.c -ln -s ../src/demime.h demime.h +for f in demime.c demime.h +do + ln -s ../src/$f $f +done # EXPERIMENTAL_* -ln -s ../src/bmi_spam.c bmi_spam.c -ln -s ../src/bmi_spam.h bmi_spam.h -ln -s ../src/spf.c spf.c -ln -s ../src/spf.h spf.h -ln -s ../src/srs.c srs.c -ln -s ../src/srs.h srs.h -ln -s ../src/dcc.c dcc.c -ln -s ../src/dcc.h dcc.h +for f in bmi_spam.c bmi_spam.h dcc.c dcc.h dane.c dane-gnu.c dane-openssl.c \ + danessl.h imap_utf7.c spf.c spf.h srs.c srs.h utf8.c +do + ln -s ../src/$f $f +done # End of MakeLinks diff -Nru exim4-4.84/src/acl.c exim4-4.86~RC4/src/acl.c --- exim4-4.84/src/acl.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/acl.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -181,17 +181,17 @@ enum { CONTROL_AUTH_UNADVERTISED, - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL CONTROL_BMI_RUN, - #endif +#endif CONTROL_DEBUG, - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM CONTROL_DKIM_VERIFY, - #endif - #ifdef EXPERIMENTAL_DMARC +#endif +#ifdef EXPERIMENTAL_DMARC CONTROL_DMARC_VERIFY, CONTROL_DMARC_FORENSIC, - #endif +#endif CONTROL_DSCP, CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, @@ -203,11 +203,14 @@ CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, CONTROL_SUPPRESS_LOCAL_FIXUPS, - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN CONTROL_NO_MBOX_UNSPOOL, - #endif +#endif CONTROL_FAKEDEFER, CONTROL_FAKEREJECT, +#ifdef EXPERIMENTAL_INTERNATIONAL + CONTROL_UTF8_DOWNCONVERT, +#endif CONTROL_NO_MULTILINE, CONTROL_NO_PIPELINING, CONTROL_NO_DELAY_FLUSH, @@ -221,17 +224,17 @@ static uschar *controls[] = { US"allow_auth_unadvertised", - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL US"bmi_run", - #endif +#endif US"debug", - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM US"dkim_disable_verify", - #endif - #ifdef EXPERIMENTAL_DMARC +#endif +#ifdef EXPERIMENTAL_DMARC US"dmarc_disable_verify", US"dmarc_enable_forensic", - #endif +#endif US"dscp", US"error", US"caseful_local_part", @@ -243,11 +246,14 @@ US"queue_only", US"submission", US"suppress_local_fixups", - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN US"no_mbox_unspool", - #endif +#endif US"fakedefer", US"fakereject", +#ifdef EXPERIMENTAL_INTERNATIONAL + US"utf8_downconvert", +#endif US"no_multiline_responses", US"no_pipelining", US"no_delay_flush", @@ -600,26 +606,26 @@ (unsigned int) ~((1<text, s, hlen) == 0) break; + if (Ustrncmp((*hptr)->text, hdr, hlen) == 0) break; hptr = &((*hptr)->next); } @@ -1130,7 +1153,7 @@ if (*hptr == NULL) { header_line *h = store_get(sizeof(header_line)); - h->text = s; + h->text = hdr; h->next = NULL; h->type = newtype; h->slen = hlen; @@ -1197,15 +1220,12 @@ */ static void -setup_remove_header(uschar *hnames) +setup_remove_header(const uschar *hnames) { if (*hnames != 0) - { - if (acl_removed_headers == NULL) - acl_removed_headers = hnames; - else - acl_removed_headers = string_sprintf("%s : %s", acl_removed_headers, hnames); - } + acl_removed_headers = acl_removed_headers + ? string_sprintf("%s : %s", acl_removed_headers, hnames) + : string_copy(hnames); } @@ -1389,9 +1409,6 @@ if (rr->type != T_A #if HAVE_IPV6 && rr->type != T_AAAA - #ifdef SUPPORT_A6 - && rr->type != T_A6 - #endif #endif ) continue; @@ -1444,10 +1461,11 @@ */ static int -acl_verify_csa(uschar *domain) +acl_verify_csa(const uschar *domain) { tree_node *t; -uschar *found, *p; +const uschar *found; +uschar *p; int priority, weight, port; dns_answer dnsa; dns_scan dnss; @@ -1470,7 +1488,7 @@ if (domain[0] == '[') { - uschar *start = Ustrchr(domain, ':'); + const uschar *start = Ustrchr(domain, ':'); if (start == NULL) start = domain; domain = string_copyn(start + 1, Ustrlen(start) - 2); } @@ -1550,7 +1568,7 @@ assertion: legitimate SMTP clients are all explicitly authorized with CSA SRV records of their own. */ - if (found != domain) + if (Ustrcmp(found, domain) != 0) { if (port & 1) return t->data.val = CSA_FAIL_EXPLICIT; @@ -1610,24 +1628,20 @@ type = T_A; -#if HAVE_IPV6 && defined(SUPPORT_A6) -DNS_LOOKUP_AGAIN: -#endif - lookup_dnssec_authenticated = NULL; switch (dns_lookup(&dnsa, target, type, NULL)) { /* If something bad happened (most commonly DNS_AGAIN), defer. */ default: - return t->data.val = CSA_DEFER_ADDR; + return t->data.val = CSA_DEFER_ADDR; /* If the query succeeded, scan the addresses and return the result. */ case DNS_SUCCEED: - rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ANSWERS, target); - if (rc != CSA_FAIL_NOADDR) return t->data.val = rc; - /* else fall through */ + rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ANSWERS, target); + if (rc != CSA_FAIL_NOADDR) return t->data.val = rc; + /* else fall through */ /* If the target has no IP addresses, the client cannot have an authorized IP address. However, if the target site uses A6 records (not AAAA records) @@ -1635,12 +1649,7 @@ case DNS_NOMATCH: case DNS_NODATA: - - #if HAVE_IPV6 && defined(SUPPORT_A6) - if (type == T_AAAA) { type = T_A6; goto DNS_LOOKUP_AGAIN; } - #endif - - return t->data.val = CSA_FAIL_NOADDR; + return t->data.val = CSA_FAIL_NOADDR; } } @@ -1662,7 +1671,7 @@ unsigned alt_opt_sep; /* >0 Non-/ option separator (custom parser) */ } verify_type_t; static verify_type_t verify_type_list[] = { - { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, ~0, TRUE, 0 }, + { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, ~0, FALSE, 0 }, { US"certificate", VERIFY_CERT, ~0, TRUE, 0 }, { US"helo", VERIFY_HELO, ~0, TRUE, 0 }, { US"csa", VERIFY_CSA, ~0, FALSE, 0 }, @@ -1726,7 +1735,7 @@ */ static int -acl_verify(int where, address_item *addr, uschar *arg, +acl_verify(int where, address_item *addr, const uschar *arg, uschar **user_msgptr, uschar **log_msgptr, int *basic_errno) { int sep = '/'; @@ -1750,7 +1759,7 @@ */ uschar *slash = Ustrchr(arg, '/'); -uschar *list = arg; +const uschar *list = arg; uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); verify_type_t * vp; @@ -1783,7 +1792,11 @@ { case VERIFY_REV_HOST_LKUP: if (sender_host_address == NULL) return OK; - return acl_verify_reverse(user_msgptr, log_msgptr); + if ((rc = acl_verify_reverse(user_msgptr, log_msgptr)) == DEFER) + while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) + if (strcmpic(ss, US"defer_ok") == 0) + return OK; + return rc; case VERIFY_CERT: /* TLS certificate verification is done at STARTTLS time; here we just @@ -1911,12 +1924,13 @@ while (isspace(*ss)) ss++; if (*ss++ == '=') { + const uschar * sublist = ss; int optsep = ','; uschar *opt; uschar buffer[256]; - while (isspace(*ss)) ss++; + while (isspace(*sublist)) sublist++; - while ((opt = string_nextinlist(&ss, &optsep, buffer, sizeof(buffer))) + while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer))) != NULL) { callout_opt_t * op; @@ -2078,6 +2092,13 @@ uschar *save_address_data = deliver_address_data; sender_vaddr = deliver_make_addr(verify_sender_address, TRUE); +#ifdef EXPERIMENTAL_INTERNATIONAL + if ((sender_vaddr->prop.utf8_msg = message_smtputf8)) + { + sender_vaddr->prop.utf8_downcvt = message_utf8_downconvert == 1; + sender_vaddr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + } +#endif if (no_details) setflag(sender_vaddr, af_sverify_told); if (verify_sender_address[0] != 0) { @@ -2134,7 +2155,7 @@ /* Put the sender address_data value into $sender_address_data */ - sender_address_data = sender_vaddr->p.address_data; + sender_address_data = sender_vaddr->prop.address_data; } /* A recipient address just gets a straightforward verify; again we must handle @@ -2164,7 +2185,7 @@ if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE; /* Make $address_data visible */ - deliver_address_data = addr2.p.address_data; + deliver_address_data = addr2.prop.address_data; } /* We have a result from the relevant test. Handle defer overrides first. */ @@ -2183,13 +2204,9 @@ if (rc != OK && verify_sender_address != NULL) { if (rc != DEFER) - { *log_msgptr = *user_msgptr = US"Sender verify failed"; - } else if (*basic_errno != ERRNO_CALLOUTDEFER) - { *log_msgptr = *user_msgptr = US"Could not complete sender verify"; - } else { *log_msgptr = US"Could not complete sender verify callout"; @@ -2239,7 +2256,7 @@ */ static int -decode_control(uschar *arg, uschar **pptr, int where, uschar **log_msgptr) +decode_control(const uschar *arg, const uschar **pptr, int where, uschar **log_msgptr) { int len; control_def *d; @@ -2324,7 +2341,7 @@ */ static int -acl_ratelimit(uschar *arg, int where, uschar **log_msgptr) +acl_ratelimit(const uschar *arg, int where, uschar **log_msgptr) { double limit, period, count; uschar *ss; @@ -2352,7 +2369,10 @@ sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0); if (sender_rate_limit == NULL) + { limit = -1.0; + ss = NULL; /* compiler quietening */ + } else { limit = Ustrtod(sender_rate_limit, &ss); @@ -2850,7 +2870,7 @@ */ static int -acl_udpsend(uschar *arg, uschar **log_msgptr) +acl_udpsend(const uschar *arg, uschar **log_msgptr) { int sep = 0; uschar *hostname; @@ -2976,15 +2996,14 @@ uschar *log_message = NULL; uschar *debug_tag = NULL; uschar *debug_opts = NULL; -uschar *p = NULL; int rc = OK; #ifdef WITH_CONTENT_SCAN -int sep = '/'; +int sep = -'/'; #endif for (; cb != NULL; cb = cb->next) { - uschar *arg; + const uschar *arg; int control_type; /* The message and log_message items set up messages to be used in @@ -3123,267 +3142,315 @@ break; case ACLC_CONTROL: - control_type = decode_control(arg, &p, where, log_msgptr); - - /* Check if this control makes sense at this time */ - - if ((control_forbids[control_type] & (1 << where)) != 0) { - *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL", - controls[control_type], acl_wherenames[where]); - return ERROR; - } + const uschar *p = NULL; + control_type = decode_control(arg, &p, where, log_msgptr); - switch(control_type) - { - case CONTROL_AUTH_UNADVERTISED: - allow_auth_unadvertised = TRUE; - break; + /* Check if this control makes sense at this time */ - #ifdef EXPERIMENTAL_BRIGHTMAIL - case CONTROL_BMI_RUN: - bmi_run = 1; - break; - #endif + if ((control_forbids[control_type] & (1 << where)) != 0) + { + *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL", + controls[control_type], acl_wherenames[where]); + return ERROR; + } - #ifndef DISABLE_DKIM - case CONTROL_DKIM_VERIFY: - dkim_disable_verify = TRUE; - #ifdef EXPERIMENTAL_DMARC - /* Since DKIM was blocked, skip DMARC too */ - dmarc_disable_verify = TRUE; - dmarc_enable_forensic = FALSE; - #endif - break; - #endif + switch(control_type) + { + case CONTROL_AUTH_UNADVERTISED: + allow_auth_unadvertised = TRUE; + break; - #ifdef EXPERIMENTAL_DMARC - case CONTROL_DMARC_VERIFY: - dmarc_disable_verify = TRUE; - break; + #ifdef EXPERIMENTAL_BRIGHTMAIL + case CONTROL_BMI_RUN: + bmi_run = 1; + break; + #endif - case CONTROL_DMARC_FORENSIC: - dmarc_enable_forensic = TRUE; - break; - #endif + #ifndef DISABLE_DKIM + case CONTROL_DKIM_VERIFY: + dkim_disable_verify = TRUE; + #ifdef EXPERIMENTAL_DMARC + /* Since DKIM was blocked, skip DMARC too */ + dmarc_disable_verify = TRUE; + dmarc_enable_forensic = FALSE; + #endif + break; + #endif - case CONTROL_DSCP: - if (*p == '/') - { - int fd, af, level, optname, value; - /* If we are acting on stdin, the setsockopt may fail if stdin is not - a socket; we can accept that, we'll just debug-log failures anyway. */ - fd = fileno(smtp_in); - af = ip_get_address_family(fd); - if (af < 0) - { - HDEBUG(D_acl) - debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n", - strerror(errno)); - break; - } - if (dscp_lookup(p+1, af, &level, &optname, &value)) - { - if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) - { - HDEBUG(D_acl) debug_printf("failed to set input DSCP[%s]: %s\n", - p+1, strerror(errno)); - } - else - { - HDEBUG(D_acl) debug_printf("set input DSCP to \"%s\"\n", p+1); - } - } - else - { - *log_msgptr = string_sprintf("unrecognised DSCP value in \"control=%s\"", arg); - return ERROR; - } - } - else - { - *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); - return ERROR; - } - break; + #ifdef EXPERIMENTAL_DMARC + case CONTROL_DMARC_VERIFY: + dmarc_disable_verify = TRUE; + break; - case CONTROL_ERROR: - return ERROR; + case CONTROL_DMARC_FORENSIC: + dmarc_enable_forensic = TRUE; + break; + #endif - case CONTROL_CASEFUL_LOCAL_PART: - deliver_localpart = addr->cc_local_part; - break; + case CONTROL_DSCP: + if (*p == '/') + { + int fd, af, level, optname, value; + /* If we are acting on stdin, the setsockopt may fail if stdin is not + a socket; we can accept that, we'll just debug-log failures anyway. */ + fd = fileno(smtp_in); + af = ip_get_address_family(fd); + if (af < 0) + { + HDEBUG(D_acl) + debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n", + strerror(errno)); + break; + } + if (dscp_lookup(p+1, af, &level, &optname, &value)) + { + if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) + { + HDEBUG(D_acl) debug_printf("failed to set input DSCP[%s]: %s\n", + p+1, strerror(errno)); + } + else + { + HDEBUG(D_acl) debug_printf("set input DSCP to \"%s\"\n", p+1); + } + } + else + { + *log_msgptr = string_sprintf("unrecognised DSCP value in \"control=%s\"", arg); + return ERROR; + } + } + else + { + *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); + return ERROR; + } + break; - case CONTROL_CASELOWER_LOCAL_PART: - deliver_localpart = addr->lc_local_part; - break; + case CONTROL_ERROR: + return ERROR; - case CONTROL_ENFORCE_SYNC: - smtp_enforce_sync = TRUE; - break; + case CONTROL_CASEFUL_LOCAL_PART: + deliver_localpart = addr->cc_local_part; + break; - case CONTROL_NO_ENFORCE_SYNC: - smtp_enforce_sync = FALSE; - break; + case CONTROL_CASELOWER_LOCAL_PART: + deliver_localpart = addr->lc_local_part; + break; - #ifdef WITH_CONTENT_SCAN - case CONTROL_NO_MBOX_UNSPOOL: - no_mbox_unspool = TRUE; - break; - #endif + case CONTROL_ENFORCE_SYNC: + smtp_enforce_sync = TRUE; + break; - case CONTROL_NO_MULTILINE: - no_multiline_responses = TRUE; - break; + case CONTROL_NO_ENFORCE_SYNC: + smtp_enforce_sync = FALSE; + break; - case CONTROL_NO_PIPELINING: - pipelining_enable = FALSE; - break; + #ifdef WITH_CONTENT_SCAN + case CONTROL_NO_MBOX_UNSPOOL: + no_mbox_unspool = TRUE; + break; + #endif - case CONTROL_NO_DELAY_FLUSH: - disable_delay_flush = TRUE; - break; + case CONTROL_NO_MULTILINE: + no_multiline_responses = TRUE; + break; - case CONTROL_NO_CALLOUT_FLUSH: - disable_callout_flush = TRUE; - break; + case CONTROL_NO_PIPELINING: + pipelining_enable = FALSE; + break; - case CONTROL_FAKEREJECT: - cancel_cutthrough_connection("fakereject"); - case CONTROL_FAKEDEFER: - fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL; - if (*p == '/') - { - uschar *pp = p + 1; - while (*pp != 0) pp++; - fake_response_text = expand_string(string_copyn(p+1, pp-p-1)); - p = pp; - } - else - { - /* Explicitly reset to default string */ - fake_response_text = US"Your message has been rejected but is being kept for evaluation.\nIf it was a legitimate message, it may still be delivered to the target recipient(s)."; - } - break; + case CONTROL_NO_DELAY_FLUSH: + disable_delay_flush = TRUE; + break; - case CONTROL_FREEZE: - deliver_freeze = TRUE; - deliver_frozen_at = time(NULL); - freeze_tell = freeze_tell_config; /* Reset to configured value */ - if (Ustrncmp(p, "/no_tell", 8) == 0) - { - p += 8; - freeze_tell = NULL; - } - if (*p != 0) - { - *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); - return ERROR; - } - cancel_cutthrough_connection("item frozen"); - break; + case CONTROL_NO_CALLOUT_FLUSH: + disable_callout_flush = TRUE; + break; - case CONTROL_QUEUE_ONLY: - queue_only_policy = TRUE; - cancel_cutthrough_connection("queueing forced"); - break; + case CONTROL_FAKEREJECT: + cancel_cutthrough_connection("fakereject"); + case CONTROL_FAKEDEFER: + fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL; + if (*p == '/') + { + const uschar *pp = p + 1; + while (*pp != 0) pp++; + fake_response_text = expand_string(string_copyn(p+1, pp-p-1)); + p = pp; + } + else + { + /* Explicitly reset to default string */ + fake_response_text = US"Your message has been rejected but is being kept for evaluation.\nIf it was a legitimate message, it may still be delivered to the target recipient(s)."; + } + break; - case CONTROL_SUBMISSION: - originator_name = US""; - submission_mode = TRUE; - while (*p == '/') - { - if (Ustrncmp(p, "/sender_retain", 14) == 0) - { - p += 14; - active_local_sender_retain = TRUE; - active_local_from_check = FALSE; - } - else if (Ustrncmp(p, "/domain=", 8) == 0) - { - uschar *pp = p + 8; - while (*pp != 0 && *pp != '/') pp++; - submission_domain = string_copyn(p+8, pp-p-8); - p = pp; - } - /* The name= option must be last, because it swallows the rest of - the string. */ - else if (Ustrncmp(p, "/name=", 6) == 0) - { - uschar *pp = p + 6; - while (*pp != 0) pp++; - submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6, - big_buffer, big_buffer_size)); - p = pp; - } - else break; - } - if (*p != 0) - { - *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); - return ERROR; - } - break; + case CONTROL_FREEZE: + deliver_freeze = TRUE; + deliver_frozen_at = time(NULL); + freeze_tell = freeze_tell_config; /* Reset to configured value */ + if (Ustrncmp(p, "/no_tell", 8) == 0) + { + p += 8; + freeze_tell = NULL; + } + if (*p != 0) + { + *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); + return ERROR; + } + cancel_cutthrough_connection("item frozen"); + break; - case CONTROL_DEBUG: - while (*p == '/') - { - if (Ustrncmp(p, "/tag=", 5) == 0) - { - uschar *pp = p + 5; - while (*pp != '\0' && *pp != '/') pp++; - debug_tag = string_copyn(p+5, pp-p-5); - p = pp; - } - else if (Ustrncmp(p, "/opts=", 6) == 0) - { - uschar *pp = p + 6; - while (*pp != '\0' && *pp != '/') pp++; - debug_opts = string_copyn(p+6, pp-p-6); - p = pp; - } - } - debug_logging_activate(debug_tag, debug_opts); - break; + case CONTROL_QUEUE_ONLY: + queue_only_policy = TRUE; + cancel_cutthrough_connection("queueing forced"); + break; - case CONTROL_SUPPRESS_LOCAL_FIXUPS: - suppress_local_fixups = TRUE; - break; + case CONTROL_SUBMISSION: + originator_name = US""; + submission_mode = TRUE; + while (*p == '/') + { + if (Ustrncmp(p, "/sender_retain", 14) == 0) + { + p += 14; + active_local_sender_retain = TRUE; + active_local_from_check = FALSE; + } + else if (Ustrncmp(p, "/domain=", 8) == 0) + { + const uschar *pp = p + 8; + while (*pp != 0 && *pp != '/') pp++; + submission_domain = string_copyn(p+8, pp-p-8); + p = pp; + } + /* The name= option must be last, because it swallows the rest of + the string. */ + else if (Ustrncmp(p, "/name=", 6) == 0) + { + const uschar *pp = p + 6; + while (*pp != 0) pp++; + submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6, + big_buffer, big_buffer_size)); + p = pp; + } + else break; + } + if (*p != 0) + { + *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); + return ERROR; + } + break; - case CONTROL_CUTTHROUGH_DELIVERY: - if (deliver_freeze) - *log_msgptr = US"frozen"; - else if (queue_only_policy) - *log_msgptr = US"queue-only"; - else if (fake_response == FAIL) - *log_msgptr = US"fakereject"; - else - { - cutthrough_delivery = TRUE; + case CONTROL_DEBUG: + while (*p == '/') + { + if (Ustrncmp(p, "/tag=", 5) == 0) + { + const uschar *pp = p + 5; + while (*pp != '\0' && *pp != '/') pp++; + debug_tag = string_copyn(p+5, pp-p-5); + p = pp; + } + else if (Ustrncmp(p, "/opts=", 6) == 0) + { + const uschar *pp = p + 6; + while (*pp != '\0' && *pp != '/') pp++; + debug_opts = string_copyn(p+6, pp-p-6); + p = pp; + } + } + debug_logging_activate(debug_tag, debug_opts); + break; + + case CONTROL_SUPPRESS_LOCAL_FIXUPS: + suppress_local_fixups = TRUE; break; + + case CONTROL_CUTTHROUGH_DELIVERY: + if (prdr_requested) + /* Too hard to think about for now. We might in future cutthrough + the case where both sides handle prdr and this-node prdr acl + is "accept" */ + *log_msgptr = string_sprintf("PRDR on %s reception\n", arg); + else + { + if (deliver_freeze) + *log_msgptr = US"frozen"; + else if (queue_only_policy) + *log_msgptr = US"queue-only"; + else if (fake_response == FAIL) + *log_msgptr = US"fakereject"; + else + { + if (rcpt_count == 1) cutthrough.delivery = TRUE; + break; + } + *log_msgptr = string_sprintf("\"control=%s\" on %s item", + arg, *log_msgptr); + } + return ERROR; + + #ifdef EXPERIMENTAL_INTERNATIONAL + case CONTROL_UTF8_DOWNCONVERT: + if (*p == '/') + { + if (p[1] == '1') + { + message_utf8_downconvert = 1; + addr->prop.utf8_downcvt = TRUE; + addr->prop.utf8_downcvt_maybe = FALSE; + p += 2; + break; + } + if (p[1] == '0') + { + message_utf8_downconvert = 0; + addr->prop.utf8_downcvt = FALSE; + addr->prop.utf8_downcvt_maybe = FALSE; + p += 2; + break; + } + if (p[1] == '-' && p[2] == '1') + { + message_utf8_downconvert = -1; + addr->prop.utf8_downcvt = FALSE; + addr->prop.utf8_downcvt_maybe = TRUE; + p += 3; + break; + } + *log_msgptr = US"bad option value for control=utf8_downconvert"; + } + else + { + message_utf8_downconvert = 1; + addr->prop.utf8_downcvt = TRUE; + addr->prop.utf8_downcvt_maybe = FALSE; + break; + } + return ERROR; + #endif + } - *log_msgptr = string_sprintf("\"control=%s\" on %s item", - arg, *log_msgptr); - return ERROR; + break; } - break; #ifdef EXPERIMENTAL_DCC case ACLC_DCC: { /* Seperate the regular expression and any optional parameters. */ - uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); + const uschar * list = arg; + uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); /* Run the dcc backend. */ rc = dcc_process(&ss); /* Modify return code based upon the existance of options. */ - while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) - != NULL) { + while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) - { - /* FAIL so that the message is passed to the next ACL */ - rc = FAIL; - } - } + rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } break; #endif @@ -3413,6 +3480,34 @@ debug_printf("delay skipped in -bh checking mode\n"); } + /* NOTE 1: Remember that we may be + dealing with stdin/stdout here, in addition to TCP/IP connections. + Also, delays may be specified for non-SMTP input, where smtp_out and + smtp_in will be NULL. Whatever is done must work in all cases. + + NOTE 2: The added feature of flushing the output before a delay must + apply only to SMTP input. Hence the test for smtp_out being non-NULL. + */ + + else + { + if (smtp_out != NULL && !disable_delay_flush) + mac_smtp_fflush(); + +#if !defined(NO_POLL_H) && defined (POLLRDHUP) + { + struct pollfd p; + nfds_t n = 0; + if (smtp_out) + { + p.fd = fileno(smtp_out); + p.events = POLLRDHUP; + n = 1; + } + if (poll(&p, n, delay*1000) > 0) + HDEBUG(D_acl) debug_printf("delay cancelled by peer close\n"); + } +#else /* It appears to be impossible to detect that a TCP/IP connection has gone away without reading from it. This means that we cannot shorten the delay below if the client goes away, because we cannot discover @@ -3422,20 +3517,10 @@ Exim process is not held up unnecessarily. However, it seems that we can't. The poll() function does not do the right thing, and in any case it is not always available. - - NOTE 1: If ever this state of affairs changes, remember that we may be - dealing with stdin/stdout here, in addition to TCP/IP connections. - Also, delays may be specified for non-SMTP input, where smtp_out and - smtp_in will be NULL. Whatever is done must work in all cases. - - NOTE 2: The added feature of flushing the output before a delay must - apply only to SMTP input. Hence the test for smtp_out being non-NULL. */ - else - { - if (smtp_out != NULL && !disable_delay_flush) mac_smtp_fflush(); while (delay > 0) delay = sleep(delay); +#endif } } } @@ -3480,7 +3565,7 @@ case ACLC_DOMAINS: rc = match_isinlist(addr->domain, &arg, 0, &domainlist_anchor, - addr->domain_cache, MCL_DOMAIN, TRUE, &deliver_domain_data); + addr->domain_cache, MCL_DOMAIN, TRUE, CUSS &deliver_domain_data); break; /* The value in tls_cipher is the full cipher name, for example, @@ -3513,24 +3598,25 @@ case ACLC_HOSTS: rc = verify_check_this_host(&arg, sender_host_cache, NULL, - (sender_host_address == NULL)? US"" : sender_host_address, &host_data); - if (host_data != NULL) host_data = string_copy_malloc(host_data); + (sender_host_address == NULL)? US"" : sender_host_address, + CUSS &host_data); + if (rc == DEFER) *log_msgptr = search_error_message; + if (host_data) host_data = string_copy_malloc(host_data); break; case ACLC_LOCAL_PARTS: rc = match_isinlist(addr->cc_local_part, &arg, 0, &localpartlist_anchor, addr->localpart_cache, MCL_LOCALPART, TRUE, - &deliver_localpart_data); + CUSS &deliver_localpart_data); break; case ACLC_LOG_REJECT_TARGET: { int logbits = 0; int sep = 0; - uschar *s = arg; + const uschar *s = arg; uschar *ss; - while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) - != NULL) + while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))) { if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN; else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC; @@ -3549,7 +3635,7 @@ case ACLC_LOGWRITE: { int logbits = 0; - uschar *s = arg; + const uschar *s = arg; if (*s == ':') { s++; @@ -3580,21 +3666,29 @@ break; #ifdef WITH_CONTENT_SCAN - case ACLC_MALWARE: + case ACLC_MALWARE: /* Run the malware backend. */ { /* Separate the regular expression and any optional parameters. */ - uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); - /* Run the malware backend. */ - rc = malware(&ss); - /* Modify return code based upon the existance of options. */ - while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) - != NULL) { - if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) - { - /* FAIL so that the message is passed to the next ACL */ - rc = FAIL; - } - } + const uschar * list = arg; + uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); + uschar *opt; + BOOL defer_ok = FALSE; + int timeout = 0; + + while ((opt = string_nextinlist(&list, &sep, NULL, 0))) + if (strcmpic(opt, US"defer_ok") == 0) + defer_ok = TRUE; + else if ( strncmpic(opt, US"tmo=", 4) == 0 + && (timeout = readconf_readtime(opt+4, '\0', FALSE)) < 0 + ) + { + *log_msgptr = string_sprintf("bad timeout value in '%s'", opt); + return ERROR; + } + + rc = malware(ss, timeout); + if (rc == DEFER && defer_ok) + rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } break; @@ -3608,8 +3702,8 @@ break; case ACLC_RECIPIENTS: - rc = match_address_list(addr->address, TRUE, TRUE, &arg, NULL, -1, 0, - &recipient_data); + rc = match_address_list((const uschar *)addr->address, TRUE, TRUE, &arg, NULL, -1, 0, + CUSS &recipient_data); break; #ifdef WITH_CONTENT_SCAN @@ -3633,8 +3727,8 @@ break; case ACLC_SENDERS: - rc = match_address_list(sender_address, TRUE, TRUE, &arg, - sender_address_cache, -1, 0, &sender_data); + rc = match_address_list((const uschar *)sender_address, TRUE, TRUE, &arg, + sender_address_cache, -1, 0, CUSS &sender_data); break; /* Connection variables must persist forever */ @@ -3652,11 +3746,12 @@ case ACLC_SPAM: { /* Seperate the regular expression and any optional parameters. */ - uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); + const uschar * list = arg; + uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); /* Run the spam backend. */ - rc = spam(&ss); + rc = spam(CUSS &ss); /* Modify return code based upon the existance of options. */ - while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) + while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) != NULL) { if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) { @@ -4052,19 +4147,12 @@ int cond; int basic_errno = 0; BOOL endpass_seen = FALSE; + BOOL acl_quit_check = level == 0 + && (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT); *log_msgptr = *user_msgptr = NULL; acl_temp_details = FALSE; - if ((where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT) && - acl->verb != ACL_ACCEPT && - acl->verb != ACL_WARN) - { - *log_msgptr = string_sprintf("\"%s\" is not allowed in a QUIT or not-QUIT ACL", - verbs[acl->verb]); - return ERROR; - } - HDEBUG(D_acl) debug_printf("processing \"%s\"\n", verbs[acl->verb]); /* Clear out any search error message from a previous check before testing @@ -4129,7 +4217,11 @@ switch(acl->verb) { case ACL_ACCEPT: - if (cond == OK || cond == DISCARD) return cond; + if (cond == OK || cond == DISCARD) + { + HDEBUG(D_acl) debug_printf("end of %s: ACCEPT\n", acl_name); + return cond; + } if (endpass_seen) { HDEBUG(D_acl) debug_printf("accept: endpass encountered - denying access\n"); @@ -4140,17 +4232,29 @@ case ACL_DEFER: if (cond == OK) { + HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name); + if (acl_quit_check) goto badquit; acl_temp_details = TRUE; return DEFER; } break; case ACL_DENY: - if (cond == OK) return FAIL; + if (cond == OK) + { + HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name); + if (acl_quit_check) goto badquit; + return FAIL; + } break; case ACL_DISCARD: - if (cond == OK || cond == DISCARD) return DISCARD; + if (cond == OK || cond == DISCARD) + { + HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name); + if (acl_quit_check) goto badquit; + return DISCARD; + } if (endpass_seen) { HDEBUG(D_acl) debug_printf("discard: endpass encountered - denying access\n"); @@ -4159,11 +4263,21 @@ break; case ACL_DROP: - if (cond == OK) return FAIL_DROP; + if (cond == OK) + { + HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name); + if (acl_quit_check) goto badquit; + return FAIL_DROP; + } break; case ACL_REQUIRE: - if (cond != OK) return cond; + if (cond != OK) + { + HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name); + if (acl_quit_check) goto badquit; + return cond; + } break; case ACL_WARN: @@ -4192,6 +4306,11 @@ HDEBUG(D_acl) debug_printf("end of %s: implicit DENY\n", acl_name); return FAIL; + +badquit: + *log_msgptr = string_sprintf("QUIT or not-QUIT teplevel ACL may not fail " + "('%s' verb used incorrectly)", verbs[acl->verb]); + return ERROR; } @@ -4201,7 +4320,7 @@ the name of an ACL followed optionally by up to 9 space-separated arguments. The name and args are separately expanded. Args go into $acl_arg globals. */ static int -acl_check_wargs(int where, address_item *addr, uschar *s, int level, +acl_check_wargs(int where, address_item *addr, const uschar *s, int level, uschar **user_msgptr, uschar **log_msgptr) { uschar * tmp; @@ -4320,9 +4439,9 @@ log_reject_target = LOG_MAIN|LOG_REJECT; #ifndef DISABLE_PRDR -if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR ) +if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR) #else -if (where == ACL_WHERE_RCPT ) +if (where == ACL_WHERE_RCPT) #endif { adb = address_defaults; @@ -4333,6 +4452,13 @@ *log_msgptr = US"defer in percent_hack_domains check"; return DEFER; } +#ifdef EXPERIMENTAL_INTERNATIONAL + if ((addr->prop.utf8_msg = message_smtputf8)) + { + addr->prop.utf8_downcvt = message_utf8_downconvert == 1; + addr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + } +#endif deliver_domain = addr->domain; deliver_localpart = addr->local_part; } @@ -4366,9 +4492,7 @@ #ifndef DISABLE_PRDR case ACL_WHERE_PRDR: #endif - if( rcpt_count > 1 ) - cancel_cutthrough_connection("more than one recipient"); - else if (rc == OK && cutthrough_delivery && cutthrough_fd < 0) + if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt) open_cutthrough_connection(addr); break; diff -Nru exim4-4.84/src/auths/auth-spa.c exim4-4.86~RC4/src/auths/auth-spa.c --- exim4-4.84/src/auths/auth-spa.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/auths/auth-spa.c 2015-06-27 17:01:28.000000000 +0200 @@ -368,7 +368,7 @@ static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -#define BAD -1 +#define BAD (char) -1 static const char base64val[] = { BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, diff -Nru exim4-4.84/src/auths/call_pam.c exim4-4.86~RC4/src/auths/call_pam.c --- exim4-4.84/src/auths/call_pam.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/auths/call_pam.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -36,7 +36,7 @@ this to work on Solaris 2.6, so static variables are used instead. */ static int pam_conv_had_error; -static uschar *pam_args; +static const uschar *pam_args; static BOOL pam_arg_ended; @@ -129,7 +129,7 @@ */ int -auth_call_pam(uschar *s, uschar **errptr) +auth_call_pam(const uschar *s, uschar **errptr) { pam_handle_t *pamh = NULL; struct pam_conv pamc; diff -Nru exim4-4.84/src/auths/call_pwcheck.c exim4-4.86~RC4/src/auths/call_pwcheck.c --- exim4-4.84/src/auths/call_pwcheck.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/auths/call_pwcheck.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module contains interface functions to the two Cyrus authentication @@ -88,8 +88,8 @@ */ int -auth_call_saslauthd(uschar *username, uschar *password, uschar *service, - uschar *realm, uschar **errptr) +auth_call_saslauthd(const uschar *username, const uschar *password, + const uschar *service, const uschar *realm, uschar **errptr) { uschar *reply = NULL; diff -Nru exim4-4.84/src/auths/cyrus_sasl.c exim4-4.86~RC4/src/auths/cyrus_sasl.c --- exim4-4.84/src/auths/cyrus_sasl.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/auths/cyrus_sasl.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This code was originally contributed by Matthew Byng-Maddick */ @@ -97,7 +97,7 @@ { auth_cyrus_sasl_options_block *ob = (auth_cyrus_sasl_options_block *)(ablock->options_block); -uschar *list, *listptr, *buffer; +const uschar *list, *listptr, *buffer; int rc, i; unsigned int len; uschar *rs_point, *expanded_hostname; @@ -146,7 +146,7 @@ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " "couldn't initialise Cyrus SASL server connection.", ablock->name); -rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i); +rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)&list, &len, &i); if( rc != SASL_OK ) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: " "couldn't get Cyrus SASL mechanism list.", ablock->name); diff -Nru exim4-4.84/src/auths/dovecot.c exim4-4.86~RC4/src/auths/dovecot.c --- exim4-4.84/src/auths/dovecot.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/auths/dovecot.c 2015-06-27 17:01:28.000000000 +0200 @@ -240,7 +240,7 @@ uschar *p; int nargs, tmp; int crequid = 1, cont = 1, fd, ret = DEFER; - BOOL found = FALSE; + BOOL found = FALSE, have_mech_line = FALSE; HDEBUG(D_auth) debug_printf("dovecot authentication\n"); @@ -300,8 +300,20 @@ OUT("authentication socket protocol version mismatch"); } else if (Ustrcmp(args[0], US"MECH") == 0) { CHECK_COMMAND("MECH", 1, INT_MAX); + have_mech_line = TRUE; if (strcmpic(US args[1], ablock->public_name) == 0) found = TRUE; + } else if (Ustrcmp(args[0], US"SPID") == 0) { + /* Unfortunately the auth protocol handshake wasn't designed well + to differentiate between auth-client/userdb/master. auth-userdb + and auth-master send VERSION + SPID lines only and nothing + afterwards, while auth-client sends VERSION + MECH + SPID + + CUID + more. The simplest way that we can determine if we've + connected to the correct socket is to see if MECH line exists or + not (alternatively we'd have to have a small timeout after SPID + to see if CUID is sent or not). */ + if (!have_mech_line) + OUT("authentication socket type mismatch (connected to auth-master instead of auth-client)"); } else if (Ustrcmp(args[0], US"DONE") == 0) { CHECK_COMMAND("DONE", 0, 0); cont = 0; diff -Nru exim4-4.84/src/auths/Makefile exim4-4.86~RC4/src/auths/Makefile --- exim4-4.84/src/auths/Makefile 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/auths/Makefile 2015-06-27 17:01:28.000000000 +0200 @@ -9,7 +9,7 @@ call_radius.o check_serv_cond.o cram_md5.o cyrus_sasl.o dovecot.o \ get_data.o get_no64_data.o gsasl_exim.o heimdal_gssapi.o \ md5.o plaintext.o pwcheck.o sha1.o \ - spa.o xtextdecode.o xtextencode.o + spa.o tls.o xtextdecode.o xtextencode.o auths.a: $(OBJ) @$(RM_COMMAND) -f auths.a @@ -43,5 +43,6 @@ heimdal_gssapi.o: $(HDRS) heimdal_gssapi.c heimdal_gssapi.h plaintext.o: $(HDRS) plaintext.c plaintext.h spa.o: $(HDRS) spa.c spa.h +tls.o: $(HDRS) tls.c tls.h # End diff -Nru exim4-4.84/src/auths/plaintext.c exim4-4.86~RC4/src/auths/plaintext.c --- exim4-4.84/src/auths/plaintext.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/auths/plaintext.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -66,7 +66,7 @@ { auth_plaintext_options_block *ob = (auth_plaintext_options_block *)(ablock->options_block); -uschar *prompts = ob->server_prompts; +const uschar *prompts = ob->server_prompts; uschar *clear, *end, *s; int number = 1; int len, rc; @@ -76,7 +76,7 @@ if (prompts != NULL) { - prompts = expand_string(prompts); + prompts = expand_cstring(prompts); if (prompts == NULL) { auth_defer_msg = expand_string_message; @@ -163,7 +163,7 @@ { auth_plaintext_options_block *ob = (auth_plaintext_options_block *)(ablock->options_block); -uschar *text = ob->client_send; +const uschar *text = ob->client_send; uschar *s; BOOL first = TRUE; int sep = 0; diff -Nru exim4-4.84/src/auths/tls.c exim4-4.86~RC4/src/auths/tls.c --- exim4-4.84/src/auths/tls.c 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/auths/tls.c 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,80 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* This file provides an Exim authenticator driver for +a server to verify a client SSL certificate +*/ + + +#include "../exim.h" +#include "tls.h" + +/* Options specific to the tls authentication mechanism. */ + +optionlist auth_tls_options[] = { + { "server_param", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param1)) }, + { "server_param1", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param1)) }, + { "server_param2", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param2)) }, + { "server_param3", opt_stringptr, + (void *)(offsetof(auth_tls_options_block, server_param3)) }, +}; + +/* Size of the options list. An extern variable has to be used so that its +address can appear in the tables drtables.c. */ + +int auth_tls_options_count = nelem(auth_tls_options); + +/* Default private options block for the authentication method. */ + +auth_tls_options_block auth_tls_option_defaults = { + NULL, /* server_param1 */ + NULL, /* server_param2 */ + NULL, /* server_param3 */ +}; + + +/************************************************* +* Initialization entry point * +*************************************************/ + +/* Called for each instance, after its options have been read, to +enable consistency checks to be done, or anything else that needs +to be set up. */ + +void +auth_tls_init(auth_instance *ablock) +{ +ablock->public_name = ablock->name; /* needed for core code */ +} + + + +/************************************************* +* Server entry point * +*************************************************/ + +/* For interface, see auths/README */ + +int +auth_tls_server(auth_instance *ablock, uschar *data) +{ +auth_tls_options_block * ob = (auth_tls_options_block *)ablock->options_block; + +if (ob->server_param1) + auth_vars[expand_nmax++] = expand_string(ob->server_param1); +if (ob->server_param2) + auth_vars[expand_nmax++] = expand_string(ob->server_param2); +if (ob->server_param2) + auth_vars[expand_nmax++] = expand_string(ob->server_param3); +return auth_check_serv_cond(ablock); +} + + +/* End of tls.c */ diff -Nru exim4-4.84/src/auths/tls.h exim4-4.86~RC4/src/auths/tls.h --- exim4-4.84/src/auths/tls.h 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/auths/tls.h 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,30 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* Private structure for the private options. */ + +typedef struct { + uschar * server_param1; + uschar * server_param2; + uschar * server_param3; +} auth_tls_options_block; + +/* Data for reading the private options. */ + +extern optionlist auth_tls_options[]; +extern int auth_tls_options_count; + +/* Block containing default values. */ + +extern auth_tls_options_block auth_tls_option_defaults; + +/* The entry points for the mechanism */ + +extern void auth_tls_init(auth_instance *); +extern int auth_tls_server(auth_instance *, uschar *); + +/* End of sa.h */ diff -Nru exim4-4.84/src/child.c exim4-4.86~RC4/src/child.c --- exim4-4.84/src/child.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/child.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -141,7 +141,7 @@ failure. We know that there will always be at least one extra option in the call when exec() is done here, so it can be used to add to the panic data. */ -DEBUG(D_exec) debug_print_argv(argv); +DEBUG(D_exec) debug_print_argv(CUSS argv); exim_nullstd(); /* Make sure std{in,out,err} exist */ execv(CS argv[0], (char *const *)argv); @@ -307,8 +307,9 @@ */ pid_t -child_open_uid(uschar **argv, uschar **envp, int newumask, uid_t *newuid, - gid_t *newgid, int *infdptr, int *outfdptr, uschar *wd, BOOL make_leader) +child_open_uid(const uschar **argv, const uschar **envp, int newumask, + uid_t *newuid, gid_t *newgid, int *infdptr, int *outfdptr, uschar *wd, + BOOL make_leader) { int save_errno; int inpfd[2], outpfd[2]; @@ -387,7 +388,7 @@ /* Now do the exec */ if (envp == NULL) execv(CS argv[0], (char *const *)argv); - else execve(CS argv[0], (char *const *)argv, (char *const *)envp); + else execve(CS argv[0], (char *const *)argv, (char *const *)envp); /* Failed to execv. Signal this failure using EX_EXECFAILED. We are losing the actual errno we got back, because there is no way to return @@ -450,8 +451,8 @@ child_open(uschar **argv, uschar **envp, int newumask, int *infdptr, int *outfdptr, BOOL make_leader) { -return child_open_uid(argv, envp, newumask, NULL, NULL, infdptr, outfdptr, - NULL, make_leader); +return child_open_uid(CUSS argv, CUSS envp, newumask, NULL, NULL, + infdptr, outfdptr, NULL, make_leader); } diff -Nru exim4-4.84/src/config.h.defaults exim4-4.86~RC4/src/config.h.defaults --- exim4-4.84/src/config.h.defaults 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/config.h.defaults 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* The default settings for Exim configuration variables. A #define without @@ -24,6 +24,7 @@ #define AUTH_HEIMDAL_GSSAPI #define AUTH_PLAINTEXT #define AUTH_SPA +#define AUTH_TLS #define AUTH_VARS 3 @@ -167,15 +168,16 @@ /* EXPERIMENTAL features */ #define EXPERIMENTAL_BRIGHTMAIL -#define EXPERIMENTAL_CERTNAMES +#define EXPERIMENTAL_DANE #define EXPERIMENTAL_DCC #define EXPERIMENTAL_DMARC -#define EXPERIMENTAL_DSN +#define EXPERIMENTAL_EVENT +#define EXPERIMENTAL_INTERNATIONAL #define EXPERIMENTAL_PROXY #define EXPERIMENTAL_REDIS +#define EXPERIMENTAL_SOCKS #define EXPERIMENTAL_SPF #define EXPERIMENTAL_SRS -#define EXPERIMENTAL_TPDA /* For developers */ #define WANT_DEEPER_PRINTF_CHECKS diff -Nru exim4-4.84/src/configure.default exim4-4.86~RC4/src/configure.default --- exim4-4.84/src/configure.default 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/configure.default 2015-06-27 17:01:28.000000000 +0200 @@ -221,18 +221,26 @@ host_lookup = * -# The settings below, which are actually the same as the defaults in the -# code, cause Exim to make RFC 1413 (ident) callbacks for all incoming SMTP -# calls. You can limit the hosts to which these calls are made, and/or change -# the timeout that is used. If you set the timeout to zero, all RFC 1413 calls -# are disabled. RFC 1413 calls are cheap and can provide useful information -# for tracing problem messages, but some hosts and firewalls have problems -# with them. This can result in a timeout instead of an immediate refused -# connection, leading to delays on starting up SMTP sessions. (The default was -# reduced from 30s to 5s for release 4.61.) +# The settings below cause Exim to make RFC 1413 (ident) callbacks +# for all incoming SMTP calls. You can limit the hosts to which these +# calls are made, and/or change the timeout that is used. If you set +# the timeout to zero, all RFC 1413 calls are disabled. RFC 1413 calls +# are cheap and can provide useful information for tracing problem +# messages, but some hosts and firewalls have problems with them. +# This can result in a timeout instead of an immediate refused +# connection, leading to delays on starting up SMTP sessions. +# (The default was reduced from 30s to 5s for release 4.61. and to +# disabled for release 4.86) +# +#rfc1413_hosts = * +#rfc1413_query_timeout = 5s + -rfc1413_hosts = * -rfc1413_query_timeout = 5s +# Enable an efficiency feature. We advertise the feature; clients +# may request to use it. For multi-recipient mails we then can +# reject or accept per-user after the message is received. +# +prdr_enable = true # By default, Exim expects all envelope addresses to be fully qualified, that @@ -248,6 +256,13 @@ # and/or qualify_recipient (see above). +# Unless you run a high-volume site you probably want more logging +# detail than the default. Adjust to suit. + +log_selector = +smtp_protocol_error +smtp_syntax_error \ + +tls_certificate_verified + + # If you want Exim to support the "percent hack" for certain domains, # uncomment the following line and provide a list of domains. The "percent # hack" is the feature by which mail addressed to x%y@z (where z is one of diff -Nru exim4-4.84/src/daemon.c exim4-4.86~RC4/src/daemon.c --- exim4-4.84/src/daemon.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/daemon.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with running Exim as a daemon */ @@ -1048,7 +1048,7 @@ int sep; int pct = 0; uschar *s; - uschar *list; + const uschar * list; uschar *local_iface_source = US"local_interfaces"; ip_address_item *ipa; ip_address_item **pipa; @@ -1071,8 +1071,7 @@ list = override_local_interfaces; sep = 0; - while ((s = string_nextinlist(&list,&sep,big_buffer,big_buffer_size)) - != NULL) + while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) { uschar joinstr[4]; uschar **ptr; @@ -1127,13 +1126,13 @@ list = daemon_smtp_port; sep = 0; - while ((s = string_nextinlist(&list,&sep,big_buffer,big_buffer_size))) + while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) pct++; default_smtp_port = store_get((pct+1) * sizeof(int)); list = daemon_smtp_port; sep = 0; for (pct = 0; - (s = string_nextinlist(&list,&sep,big_buffer,big_buffer_size)); + (s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)); pct++) { if (isdigit(*s)) @@ -1700,6 +1699,15 @@ readconf_printtime(queue_interval)); } +/* Do any work it might be useful to amortize over our children +(eg: compile regex) */ + +deliver_init(); +dns_pattern_init(); + +#ifdef WITH_CONTENT_SCAN +malware_init(); +#endif /* Close the log so it can be renamed and moved. In the few cases below where this long-running process writes to the log (always exceptional conditions), it diff -Nru exim4-4.84/src/dane.c exim4-4.86~RC4/src/dane.c --- exim4-4.84/src/dane.c 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/dane.c 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,50 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2012, 2014 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* This module provides DANE (RFC6659) support for Exim. See also +the draft RFC for DANE-over-SMTP, "SMTP security via opportunistic DANE TLS" +(V. Dukhovni, W. Hardaker) - version 10, dated May 25, 2014. + +The code for DANE support with Openssl was provided by V.Dukhovni. + +No cryptographic code is included in Exim. All this module does is to call +functions from the OpenSSL or GNU TLS libraries. */ + + +#include "exim.h" + +/* This module is compiled only when it is specifically requested in the +build-time configuration. However, some compilers don't like compiling empty +modules, so keep them happy with a dummy when skipping the rest. Make it +reference itself to stop picky compilers complaining that it is unused, and put +in a dummy argument to stop even pickier compilers complaining about infinite +loops. */ + +#ifndef EXPERIMENTAL_DANE +static void dummy(int x) { dummy(x-1); } +#else + +/* Enabling DANE without enabling TLS cannot work. Abort the compilation. */ +# ifndef SUPPORT_TLS +# error DANE support requires that TLS support must be enabled. Abort build. +# endif + +/* DNSSEC support is also required */ +# ifndef RES_USE_DNSSEC +# error DANE support requires that the DNS reolver library supports DNSSEC +# endif + +# ifdef USE_GNUTLS +# include "dane-gnu.c" +# else +# include "dane-openssl.c" +# endif + + +#endif /* EXPERIMENTAL_DANE */ + +/* End of dane.c */ diff -Nru exim4-4.84/src/dane-gnu.c exim4-4.86~RC4/src/dane-gnu.c --- exim4-4.84/src/dane-gnu.c 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/dane-gnu.c 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,21 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) University of Cambridge 1995 - 2013 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* This file (will) provide DANE support for Exim using the GnuTLS library, +but is not yet an available supported implementation. This file is #included +into dane.c when USE_GNUTLS has been set. */ + +/* As of March 2014, the reference implementation for DANE that we are +using was written by Viktor Dukhovny and it supports OpenSSL only. At +some point we will add GnuTLS support, but for right now just abort the +build and explain why. */ + + +#error No support for DANE using GnuTLS yet. + + +/* End of dane-gnu.c */ diff -Nru exim4-4.84/src/dane-openssl.c exim4-4.86~RC4/src/dane-openssl.c --- exim4-4.84/src/dane-openssl.c 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/dane-openssl.c 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,1520 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x1000000fL +# error "OpenSSL 1.0.0 or higher required" +#else /* remainder of file */ + +#include "danessl.h" + +#define DANE_F_ADD_SKID 100 +#define DANE_F_CHECK_END_ENTITY 101 +#define DANE_F_GROW_CHAIN 102 +#define DANE_F_LIST_ALLOC 103 +#define DANE_F_MATCH 104 +#define DANE_F_PUSH_EXT 105 +#define DANE_F_SET_TRUST_ANCHOR 106 +#define DANE_F_SSL_CTX_DANE_INIT 107 +#define DANE_F_SSL_DANE_ADD_TLSA 108 +#define DANE_F_SSL_DANE_INIT 109 +#define DANE_F_SSL_DANE_LIBRARY_INIT 110 +#define DANE_F_VERIFY_CERT 111 +#define DANE_F_WRAP_CERT 112 + +#define DANE_R_BAD_CERT 100 +#define DANE_R_BAD_CERT_PKEY 101 +#define DANE_R_BAD_DATA_LENGTH 102 +#define DANE_R_BAD_DIGEST 103 +#define DANE_R_BAD_NULL_DATA 104 +#define DANE_R_BAD_PKEY 105 +#define DANE_R_BAD_SELECTOR 106 +#define DANE_R_BAD_USAGE 107 +#define DANE_R_DANE_INIT 108 +#define DANE_R_DANE_SUPPORT 109 +#define DANE_R_LIBRARY_INIT 110 +#define DANE_R_NOSIGN_KEY 111 +#define DANE_R_SCTX_INIT 112 + +#ifndef OPENSSL_NO_ERR +# define DANE_F_PLACEHOLDER 0 /* FIRST! Value TBD */ +static ERR_STRING_DATA dane_str_functs[] = +{ + {DANE_F_PLACEHOLDER, "DANE library"}, /* FIRST!!! */ + {DANE_F_ADD_SKID, "add_skid"}, + {DANE_F_CHECK_END_ENTITY, "check_end_entity"}, + {DANE_F_GROW_CHAIN, "grow_chain"}, + {DANE_F_LIST_ALLOC, "list_alloc"}, + {DANE_F_MATCH, "match"}, + {DANE_F_PUSH_EXT, "push_ext"}, + {DANE_F_SET_TRUST_ANCHOR, "set_trust_anchor"}, + {DANE_F_SSL_CTX_DANE_INIT, "SSL_CTX_dane_init"}, + {DANE_F_SSL_DANE_ADD_TLSA, "SSL_dane_add_tlsa"}, + {DANE_F_SSL_DANE_INIT, "SSL_dane_init"}, + {DANE_F_SSL_DANE_LIBRARY_INIT, "SSL_dane_library_init"}, + {DANE_F_VERIFY_CERT, "verify_cert"}, + {DANE_F_WRAP_CERT, "wrap_cert"}, + {0, NULL} +}; +static ERR_STRING_DATA dane_str_reasons[] = +{ + {DANE_R_BAD_CERT, "Bad TLSA record certificate"}, + {DANE_R_BAD_CERT_PKEY, "Bad TLSA record certificate public key"}, + {DANE_R_BAD_DATA_LENGTH, "Bad TLSA record digest length"}, + {DANE_R_BAD_DIGEST, "Bad TLSA record digest"}, + {DANE_R_BAD_NULL_DATA, "Bad TLSA record null data"}, + {DANE_R_BAD_PKEY, "Bad TLSA record public key"}, + {DANE_R_BAD_SELECTOR, "Bad TLSA record selector"}, + {DANE_R_BAD_USAGE, "Bad TLSA record usage"}, + {DANE_R_DANE_INIT, "SSL_dane_init() required"}, + {DANE_R_DANE_SUPPORT, "DANE library features not supported"}, + {DANE_R_LIBRARY_INIT, "SSL_dane_library_init() required"}, + {DANE_R_SCTX_INIT, "SSL_CTX_dane_init() required"}, + {DANE_R_NOSIGN_KEY, "Certificate usage 2 requires EC support"}, + {0, NULL} +}; +#endif /*OPENSSL_NO_ERR*/ + +#define DANEerr(f, r) ERR_PUT_error(err_lib_dane, (f), (r), __FILE__, __LINE__) + +static int err_lib_dane = -1; +static int dane_idx = -1; + +#ifdef X509_V_FLAG_PARTIAL_CHAIN /* OpenSSL >= 1.0.2 */ +static int wrap_to_root = 0; +#else +static int wrap_to_root = 1; +#endif + +static void (*cert_free)(void *) = (void (*)(void *)) X509_free; +static void (*pkey_free)(void *) = (void (*)(void *)) EVP_PKEY_free; + +typedef struct dane_list +{ + struct dane_list *next; + void *value; +} *dane_list; + +#define LINSERT(h, e) do { (e)->next = (h); (h) = (e); } while (0) + +typedef struct dane_host_list +{ + struct dane_host_list *next; + char *value; +} *dane_host_list; + +typedef struct dane_data +{ + size_t datalen; + unsigned char data[0]; +} *dane_data; + +typedef struct dane_data_list +{ + struct dane_data_list *next; + dane_data value; +} *dane_data_list; + +typedef struct dane_mtype +{ + int mdlen; + const EVP_MD *md; + dane_data_list data; +} *dane_mtype; + +typedef struct dane_mtype_list +{ + struct dane_mtype_list *next; + dane_mtype value; +} *dane_mtype_list; + +typedef struct dane_selector +{ + uint8_t selector; + dane_mtype_list mtype; +} *dane_selector; + +typedef struct dane_selector_list +{ + struct dane_selector_list *next; + dane_selector value; +} *dane_selector_list; + +typedef struct dane_pkey_list +{ + struct dane_pkey_list *next; + EVP_PKEY *value; +} *dane_pkey_list; + +typedef struct dane_cert_list +{ + struct dane_cert_list *next; + X509 *value; +} *dane_cert_list; + +typedef struct ssl_dane +{ + int (*verify)(X509_STORE_CTX *); + STACK_OF(X509) *roots; + STACK_OF(X509) *chain; + const char *thost; /* TLSA base domain */ + char *mhost; /* Matched, peer name */ + dane_pkey_list pkeys; + dane_cert_list certs; + dane_host_list hosts; + dane_selector_list selectors[SSL_DANE_USAGE_LAST + 1]; + int depth; + int multi; /* Multi-label wildcards? */ + int count; /* Number of TLSA records */ +} ssl_dane; + +#ifndef X509_V_ERR_HOSTNAME_MISMATCH +# define X509_V_ERR_HOSTNAME_MISMATCH X509_V_ERR_APPLICATION_VERIFICATION +#endif + +static int +match(dane_selector_list slist, X509 *cert, int depth) +{ +int matched; + +/* + * Note, set_trust_anchor() needs to know whether the match was for a + * pkey digest or a certificate digest. We return MATCHED_PKEY or + * MATCHED_CERT accordingly. + */ +#define MATCHED_CERT (SSL_DANE_SELECTOR_CERT + 1) +#define MATCHED_PKEY (SSL_DANE_SELECTOR_SPKI + 1) + +/* + * Loop over each selector, mtype, and associated data element looking + * for a match. + */ +for(matched = 0; !matched && slist; slist = slist->next) + { + dane_mtype_list m; + unsigned char mdbuf[EVP_MAX_MD_SIZE]; + unsigned char *buf = NULL; + unsigned char *buf2; + unsigned int len = 0; + + /* + * Extract ASN.1 DER form of certificate or public key. + */ + switch(slist->value->selector) + { + case SSL_DANE_SELECTOR_CERT: + len = i2d_X509(cert, NULL); + buf2 = buf = (unsigned char *) OPENSSL_malloc(len); + if(buf) i2d_X509(cert, &buf2); + break; + case SSL_DANE_SELECTOR_SPKI: + len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); + buf2 = buf = (unsigned char *) OPENSSL_malloc(len); + if(buf) i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &buf2); + break; + } + + if(!buf) + { + DANEerr(DANE_F_MATCH, ERR_R_MALLOC_FAILURE); + return 0; + } + OPENSSL_assert(buf2 - buf == len); + + /* + * Loop over each mtype and data element + */ + for(m = slist->value->mtype; !matched && m; m = m->next) + { + dane_data_list d; + unsigned char *cmpbuf = buf; + unsigned int cmplen = len; + + /* + * If it is a digest, compute the corresponding digest of the + * DER data for comparison, otherwise, use the full object. + */ + if(m->value->md) + { + cmpbuf = mdbuf; + if(!EVP_Digest(buf, len, cmpbuf, &cmplen, m->value->md, 0)) + matched = -1; + } + for(d = m->value->data; !matched && d; d = d->next) + if( cmplen == d->value->datalen + && memcmp(cmpbuf, d->value->data, cmplen) == 0) + matched = slist->value->selector + 1; + } + + OPENSSL_free(buf); + } + +return matched; +} + +static int +push_ext(X509 *cert, X509_EXTENSION *ext) +{ +X509_EXTENSIONS *exts; + +if(ext) + { + if(!(exts = cert->cert_info->extensions)) + exts = cert->cert_info->extensions = sk_X509_EXTENSION_new_null(); + if (exts && sk_X509_EXTENSION_push(exts, ext)) + return 1; + X509_EXTENSION_free(ext); + } +DANEerr(DANE_F_PUSH_EXT, ERR_R_MALLOC_FAILURE); +return 0; +} + +static int +add_ext(X509 *issuer, X509 *subject, int ext_nid, char *ext_val) +{ +X509V3_CTX v3ctx; + +X509V3_set_ctx(&v3ctx, issuer, subject, 0, 0, 0); +return push_ext(subject, X509V3_EXT_conf_nid(0, &v3ctx, ext_nid, ext_val)); +} + +static int +set_serial(X509 *cert, AUTHORITY_KEYID *akid, X509 *subject) +{ +int ret = 0; +BIGNUM *bn; + +if(akid && akid->serial) + return (X509_set_serialNumber(cert, akid->serial)); + +/* + * Add one to subject's serial to avoid collisions between TA serial and + * serial of signing root. + */ +if( (bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(subject), 0)) != 0 + && BN_add_word(bn, 1) + && BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(cert))) + ret = 1; + +if(bn) + BN_free(bn); +return ret; +} + +static int +add_akid(X509 *cert, AUTHORITY_KEYID *akid) +{ +int nid = NID_authority_key_identifier; +ASN1_STRING *id; +unsigned char c = 0; +int ret = 0; + +/* + * 0 will never be our subject keyid from a SHA-1 hash, but it could be + * our subject keyid if forced from child's akid. If so, set our + * authority keyid to 1. This way we are never self-signed, and thus + * exempt from any potential (off by default for now in OpenSSL) + * self-signature checks! + */ +id = (ASN1_STRING *) ((akid && akid->keyid) ? akid->keyid : 0); +if(id && M_ASN1_STRING_length(id) == 1 && *M_ASN1_STRING_data(id) == c) + c = 1; + +if( (akid = AUTHORITY_KEYID_new()) != 0 + && (akid->keyid = ASN1_OCTET_STRING_new()) != 0 + && M_ASN1_OCTET_STRING_set(akid->keyid, (void *) &c, 1) + && X509_add1_ext_i2d(cert, nid, akid, 0, X509V3_ADD_APPEND)) + ret = 1; +if(akid) + AUTHORITY_KEYID_free(akid); +return ret; +} + +static int +add_skid(X509 *cert, AUTHORITY_KEYID *akid) +{ +int nid = NID_subject_key_identifier; + +if(!akid || !akid->keyid) + return add_ext(0, cert, nid, "hash"); +return X509_add1_ext_i2d(cert, nid, akid->keyid, 0, X509V3_ADD_APPEND) > 0; +} + +static X509_NAME * +akid_issuer_name(AUTHORITY_KEYID *akid) +{ +if(akid && akid->issuer) + { + int i; + GENERAL_NAMES *gens = akid->issuer; + + for(i = 0; i < sk_GENERAL_NAME_num(gens); ++i) + { + GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); + + if(gn->type == GEN_DIRNAME) + return (gn->d.dirn); + } + } +return 0; +} + +static int +set_issuer_name(X509 *cert, AUTHORITY_KEYID *akid) +{ +X509_NAME *name = akid_issuer_name(akid); + +/* + * If subject's akid specifies an authority key identifer issuer name, we + * must use that. + */ +return X509_set_issuer_name(cert, + name ? name : X509_get_subject_name(cert)); +} + +static int +grow_chain(ssl_dane *dane, int trusted, X509 *cert) +{ +STACK_OF(X509) **xs = trusted ? &dane->roots : &dane->chain; +static ASN1_OBJECT *serverAuth = 0; + +#define UNTRUSTED 0 +#define TRUSTED 1 + +if( trusted && !serverAuth + && !(serverAuth = OBJ_nid2obj(NID_server_auth))) + { + DANEerr(DANE_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); + return 0; + } +if(!*xs && !(*xs = sk_X509_new_null())) + { + DANEerr(DANE_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); + return 0; + } + +if(cert) + { + if(trusted && !X509_add1_trust_object(cert, serverAuth)) + return 0; + CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + if (!sk_X509_push(*xs, cert)) + { + X509_free(cert); + DANEerr(DANE_F_GROW_CHAIN, ERR_R_MALLOC_FAILURE); + return 0; + } + } +return 1; +} + +static int +wrap_issuer(ssl_dane *dane, EVP_PKEY *key, X509 *subject, int depth, int top) +{ +int ret = 1; +X509 *cert = 0; +AUTHORITY_KEYID *akid; +X509_NAME *name = X509_get_issuer_name(subject); +EVP_PKEY *newkey = key ? key : X509_get_pubkey(subject); + +#define WRAP_MID 0 /* Ensure intermediate. */ +#define WRAP_TOP 1 /* Ensure self-signed. */ + +if(!name || !newkey || !(cert = X509_new())) + return 0; + +/* + * Record the depth of the trust-anchor certificate. + */ +if(dane->depth < 0) + dane->depth = depth + 1; + +/* + * XXX: Uncaught error condition: + * + * The return value is NULL both when the extension is missing, and when + * OpenSSL rans out of memory while parsing the extension. + */ +ERR_clear_error(); +akid = X509_get_ext_d2i(subject, NID_authority_key_identifier, 0, 0); +/* XXX: Should we peek at the error stack here??? */ + +/* + * If top is true generate a self-issued root CA, otherwise an + * intermediate CA and possibly its self-signed issuer. + * + * CA cert valid for +/- 30 days + */ +if( !X509_set_version(cert, 2) + || !set_serial(cert, akid, subject) + || !X509_set_subject_name(cert, name) + || !set_issuer_name(cert, akid) + || !X509_gmtime_adj(X509_get_notBefore(cert), -30 * 86400L) + || !X509_gmtime_adj(X509_get_notAfter(cert), 30 * 86400L) + || !X509_set_pubkey(cert, newkey) + || !add_ext(0, cert, NID_basic_constraints, "CA:TRUE") + || (!top && !add_akid(cert, akid)) + || !add_skid(cert, akid) + || ( !top && wrap_to_root + && !wrap_issuer(dane, newkey, cert, depth, WRAP_TOP))) + ret = 0; + +if(akid) + AUTHORITY_KEYID_free(akid); +if(!key) + EVP_PKEY_free(newkey); +if(ret) + ret = grow_chain(dane, !top && wrap_to_root ? UNTRUSTED : TRUSTED, cert); +if(cert) + X509_free(cert); +return ret; +} + +static int +wrap_cert(ssl_dane *dane, X509 *tacert, int depth) +{ +if(dane->depth < 0) + dane->depth = depth + 1; + +/* + * If the TA certificate is self-issued, or need not be, use it directly. + * Otherwise, synthesize requisuite ancestors. + */ +if( !wrap_to_root + || X509_check_issued(tacert, tacert) == X509_V_OK) + return grow_chain(dane, TRUSTED, tacert); + +if(wrap_issuer(dane, 0, tacert, depth, WRAP_MID)) + return grow_chain(dane, UNTRUSTED, tacert); +return 0; +} + +static int +ta_signed(ssl_dane *dane, X509 *cert, int depth) +{ +dane_cert_list x; +dane_pkey_list k; +EVP_PKEY *pk; +int done = 0; + +/* + * First check whether issued and signed by a TA cert, this is cheaper + * than the bare-public key checks below, since we can determine whether + * the candidate TA certificate issued the certificate to be checked + * first (name comparisons), before we bother with signature checks + * (public key operations). + */ +for (x = dane->certs; !done && x; x = x->next) + { + if(X509_check_issued(x->value, cert) == X509_V_OK) + { + if(!(pk = X509_get_pubkey(x->value))) + { + /* + * The cert originally contained a valid pkey, which does + * not just vanish, so this is most likely a memory error. + */ + done = -1; + break; + } + /* Check signature, since some other TA may work if not this. */ + if(X509_verify(cert, pk) > 0) + done = wrap_cert(dane, x->value, depth) ? 1 : -1; + EVP_PKEY_free(pk); + } + } + +/* + * With bare TA public keys, we can't check whether the trust chain is + * issued by the key, but we can determine whether it is signed by the + * key, so we go with that. + * + * Ideally, the corresponding certificate was presented in the chain, and we + * matched it by its public key digest one level up. This code is here + * to handle adverse conditions imposed by sloppy administrators of + * receiving systems with poorly constructed chains. + * + * We'd like to optimize out keys that should not match when the cert's + * authority key id does not match the key id of this key computed via + * the RFC keyid algorithm (SHA-1 digest of public key bit-string sans + * ASN1 tag and length thus also excluding the unused bits field that is + * logically part of the length). However, some CAs have a non-standard + * authority keyid, so we lose. Too bad. + * + * This may push errors onto the stack when the certificate signature is + * not of the right type or length, throw these away, + */ +for(k = dane->pkeys; !done && k; k = k->next) + if(X509_verify(cert, k->value) > 0) + done = wrap_issuer(dane, k->value, cert, depth, WRAP_MID) ? 1 : -1; + else + ERR_clear_error(); + +return done; +} + +static int +set_trust_anchor(X509_STORE_CTX *ctx, ssl_dane *dane, X509 *cert) +{ +int matched = 0; +int n; +int i; +int depth = 0; +EVP_PKEY *takey; +X509 *ca; +STACK_OF(X509) *in = ctx->untrusted; /* XXX: Accessor? */ + +if(!grow_chain(dane, UNTRUSTED, 0)) + return -1; + +/* + * Accept a degenerate case: depth 0 self-signed trust-anchor. + */ +if(X509_check_issued(cert, cert) == X509_V_OK) + { + dane->depth = 0; + matched = match(dane->selectors[SSL_DANE_USAGE_TRUSTED_CA], cert, 0); + if(matched > 0 && !grow_chain(dane, TRUSTED, cert)) + matched = -1; + return matched; + } + +/* Make a shallow copy of the input untrusted chain. */ +if(!(in = sk_X509_dup(in))) + { + DANEerr(DANE_F_SET_TRUST_ANCHOR, ERR_R_MALLOC_FAILURE); + return -1; + } + +/* + * At each iteration we consume the issuer of the current cert. This + * reduces the length of the "in" chain by one. If no issuer is found, + * we are done. We also stop when a certificate matches a TA in the + * peer's TLSA RRset. + * + * Caller ensures that the initial certificate is not self-signed. + */ +for(n = sk_X509_num(in); n > 0; --n, ++depth) + { + for(i = 0; i < n; ++i) + if(X509_check_issued(sk_X509_value(in, i), cert) == X509_V_OK) + break; + + /* + * Final untrusted element with no issuer in the peer's chain, it may + * however be signed by a pkey or cert obtained via a TLSA RR. + */ + if(i == n) + break; + + /* Peer's chain contains an issuer ca. */ + ca = sk_X509_delete(in, i); + + /* If not a trust anchor, record untrusted ca and continue. */ + if((matched = match(dane->selectors[SSL_DANE_USAGE_TRUSTED_CA], ca, depth+1)) + == 0) + { + if(grow_chain(dane, UNTRUSTED, ca)) + { + if(!X509_check_issued(ca, ca) == X509_V_OK) + { + /* Restart with issuer as subject */ + cert = ca; + continue; + } + /* Final self-signed element, skip ta_signed() check. */ + cert = 0; + } + else + matched = -1; + } + else if(matched == MATCHED_CERT) + { + if(!wrap_cert(dane, ca, depth)) + matched = -1; + } + else if(matched == MATCHED_PKEY) + { + if( !(takey = X509_get_pubkey(ca)) + || !wrap_issuer(dane, takey, cert, depth, WRAP_MID)) + { + if(takey) + EVP_PKEY_free(takey); + else + DANEerr(DANE_F_SET_TRUST_ANCHOR, ERR_R_MALLOC_FAILURE); + matched = -1; + } + } + break; + } + +/* Shallow free the duplicated input untrusted chain. */ +sk_X509_free(in); + +/* + * When the loop exits, if "cert" is set, it is not self-signed and has + * no issuer in the chain, we check for a possible signature via a DNS + * obtained TA cert or public key. + */ +if(matched == 0 && cert) + matched = ta_signed(dane, cert, depth); + +return matched; +} + +static int +check_end_entity(X509_STORE_CTX *ctx, ssl_dane *dane, X509 *cert) +{ +int matched; + +matched = match(dane->selectors[SSL_DANE_USAGE_FIXED_LEAF], cert, 0); +if(matched > 0) + if(!ctx->chain) + { + if( (ctx->chain = sk_X509_new_null()) + && sk_X509_push(ctx->chain, cert)) + CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + else + { + DANEerr(DANE_F_CHECK_END_ENTITY, ERR_R_MALLOC_FAILURE); + return -1; + } + } +return matched; +} + +static int +match_name(const char *certid, ssl_dane *dane) +{ +int multi = dane->multi; +dane_host_list hosts; + +for(hosts = dane->hosts; hosts; hosts = hosts->next) + { + int match_subdomain = 0; + const char *domain = hosts->value; + const char *parent; + int idlen; + int domlen; + + if(*domain == '.' && domain[1] != '\0') + { + ++domain; + match_subdomain = 1; + } + + /* + * Sub-domain match: certid is any sub-domain of hostname. + */ + if(match_subdomain) + { + if( (idlen = strlen(certid)) > (domlen = strlen(domain)) + 1 + && certid[idlen - domlen - 1] == '.' + && !strcasecmp(certid + (idlen - domlen), domain)) + return 1; + else + continue; + } + + /* + * Exact match and initial "*" match. The initial "*" in a certid + * matches one (if multi is false) or more hostname components under + * the condition that the certid contains multiple hostname components. + */ + if( !strcasecmp(certid, domain) + || ( certid[0] == '*' && certid[1] == '.' && certid[2] != 0 + && (parent = strchr(domain, '.')) != 0 + && (idlen = strlen(certid + 1)) <= (domlen = strlen(parent)) + && strcasecmp(multi ? parent + domlen - idlen : parent, certid+1) == 0)) + return 1; + } +return 0; +} + +static char * +check_name(char *name, int len) +{ +char *cp = name + len; + +while(len > 0 && !*--cp) + --len; /* Ignore trailing NULs */ +if(len <= 0) + return 0; +for(cp = name; *cp; cp++) + { + char c = *cp; + if (!((c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c == '.' || c == '-') || + (c == '*'))) + return 0; /* Only LDH, '.' and '*' */ + } +if(cp - name != len) /* Guard against internal NULs */ + return 0; +return name; +} + +static char * +parse_dns_name(const GENERAL_NAME *gn) +{ +if(gn->type != GEN_DNS) + return 0; +if(ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) + return 0; +return check_name((char *) ASN1_STRING_data(gn->d.ia5), + ASN1_STRING_length(gn->d.ia5)); +} + +static char * +parse_subject_name(X509 *cert) +{ +X509_NAME *name = X509_get_subject_name(cert); +X509_NAME_ENTRY *entry; +ASN1_STRING *entry_str; +unsigned char *namebuf; +int nid = NID_commonName; +int len; +int i; + +if(!name || (i = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) + return 0; +if(!(entry = X509_NAME_get_entry(name, i))) + return 0; +if(!(entry_str = X509_NAME_ENTRY_get_data(entry))) + return 0; + +if((len = ASN1_STRING_to_UTF8(&namebuf, entry_str)) < 0) + return 0; +if(len <= 0 || check_name((char *) namebuf, len) == 0) + { + OPENSSL_free(namebuf); + return 0; + } +return (char *) namebuf; +} + +static int +name_check(ssl_dane *dane, X509 *cert) +{ +int matched = 0; +BOOL got_altname = FALSE; +GENERAL_NAMES *gens; + +gens = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); +if(gens) + { + int n = sk_GENERAL_NAME_num(gens); + int i; + + for(i = 0; i < n; ++i) + { + const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); + const char *certid; + + if(gn->type != GEN_DNS) + continue; + got_altname = TRUE; + certid = parse_dns_name(gn); + if(certid && *certid) + { + if((matched = match_name(certid, dane)) == 0) + continue; + if(!(dane->mhost = OPENSSL_strdup(certid))) + matched = -1; + break; + } + } + GENERAL_NAMES_free(gens); + } + +/* + * XXX: Should the subjectName be skipped when *any* altnames are present, + * or only when DNS altnames are present? + */ +if(got_altname) + { + char *certid = parse_subject_name(cert); + if(certid != 0 && *certid && (matched = match_name(certid, dane)) != 0) + dane->mhost = certid; /* Already a copy */ + } +return matched; +} + +static int +verify_chain(X509_STORE_CTX *ctx) +{ +dane_selector_list issuer_rrs; +dane_selector_list leaf_rrs; +int (*cb)(int, X509_STORE_CTX *) = ctx->verify_cb; +int ssl_idx = SSL_get_ex_data_X509_STORE_CTX_idx(); +SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_idx); +ssl_dane *dane = SSL_get_ex_data(ssl, dane_idx); +X509 *cert = ctx->cert; /* XXX: accessor? */ +int matched = 0; +int chain_length = sk_X509_num(ctx->chain); + +DEBUG(D_tls) debug_printf("Dane verify-chain\n"); + +issuer_rrs = dane->selectors[SSL_DANE_USAGE_LIMIT_ISSUER]; +leaf_rrs = dane->selectors[SSL_DANE_USAGE_LIMIT_LEAF]; +ctx->verify = dane->verify; + +if((matched = name_check(dane, cert)) < 0) + { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); + return 0; + } + +if(!matched) + { + ctx->error_depth = 0; + ctx->current_cert = cert; + X509_STORE_CTX_set_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH); + if(!cb(0, ctx)) + return 0; + } +matched = 0; + +/* + * Satisfy at least one usage 0 or 1 constraint, unless we've already + * matched a usage 2 trust anchor. + * + * XXX: internal_verify() doesn't callback with top certs that are not + * self-issued. This should be fixed in a future OpenSSL. + */ +if(dane->roots && sk_X509_num(dane->roots)) + { +#ifndef NO_CALLBACK_WORKAROUND + X509 *top = sk_X509_value(ctx->chain, dane->depth); + + if(X509_check_issued(top, top) != X509_V_OK) + { + ctx->error_depth = dane->depth; + ctx->current_cert = top; + if(!cb(1, ctx)) + return 0; + } +#endif + /* Pop synthetic trust-anchor ancestors off the chain! */ + while (--chain_length > dane->depth) + X509_free(sk_X509_pop(ctx->chain)); + } +else if(issuer_rrs || leaf_rrs) + { + int n = chain_length; + + /* + * Check for an EE match, then a CA match at depths > 0, and + * finally, if the EE cert is self-issued, for a depth 0 CA match. + */ + if(leaf_rrs) + matched = match(leaf_rrs, cert, 0); + while(!matched && issuer_rrs && --n >= 0) + { + X509 *xn = sk_X509_value(ctx->chain, n); + + if(n > 0 || X509_check_issued(xn, xn) == X509_V_OK) + matched = match(issuer_rrs, xn, n); + } + + if(matched < 0) + { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); + return 0; + } + + if(!matched) + { + ctx->current_cert = cert; + ctx->error_depth = 0; + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_UNTRUSTED); + if(!cb(0, ctx)) + return 0; + } + } + +return ctx->verify(ctx); +} + +static int +verify_cert(X509_STORE_CTX *ctx, void *unused_ctx) +{ +static int ssl_idx = -1; +SSL *ssl; +ssl_dane *dane; +int (*cb)(int, X509_STORE_CTX *) = ctx->verify_cb; +int matched; +X509 *cert = ctx->cert; /* XXX: accessor? */ + +DEBUG(D_tls) debug_printf("Dane verify-cert\n"); + +if(ssl_idx < 0) + ssl_idx = SSL_get_ex_data_X509_STORE_CTX_idx(); +if(dane_idx < 0) + { + DANEerr(DANE_F_VERIFY_CERT, ERR_R_MALLOC_FAILURE); + return -1; + } + +ssl = X509_STORE_CTX_get_ex_data(ctx, ssl_idx); +if(!(dane = SSL_get_ex_data(ssl, dane_idx)) || !cert) + return X509_verify_cert(ctx); + +if(dane->selectors[SSL_DANE_USAGE_FIXED_LEAF]) + { + if((matched = check_end_entity(ctx, dane, cert)) > 0) + { + ctx->error_depth = 0; + ctx->current_cert = cert; + return cb(1, ctx); + } + if(matched < 0) + { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); + return -1; + } + } + +if(dane->selectors[SSL_DANE_USAGE_TRUSTED_CA]) + { + if((matched = set_trust_anchor(ctx, dane, cert)) < 0) + { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_OUT_OF_MEM); + return -1; + } + if(matched) + { + /* + * Check that setting the untrusted chain updates the expected + * structure member at the expected offset. + */ + X509_STORE_CTX_trusted_stack(ctx, dane->roots); + X509_STORE_CTX_set_chain(ctx, dane->chain); + OPENSSL_assert(ctx->untrusted == dane->chain); + } + } + +/* + * Name checks and usage 0/1 constraint enforcement are delayed until + * X509_verify_cert() builds the full chain and calls our verify_chain() + * wrapper. + */ +dane->verify = ctx->verify; +ctx->verify = verify_chain; + +return X509_verify_cert(ctx); +} + +static dane_list +list_alloc(size_t vsize) +{ +void *value = (void *) OPENSSL_malloc(vsize); +dane_list l; + +if(!value) + { + DANEerr(DANE_F_LIST_ALLOC, ERR_R_MALLOC_FAILURE); + return 0; + } +if(!(l = (dane_list) OPENSSL_malloc(sizeof(*l)))) + { + OPENSSL_free(value); + DANEerr(DANE_F_LIST_ALLOC, ERR_R_MALLOC_FAILURE); + return 0; + } +l->next = 0; +l->value = value; +return l; +} + +static void +list_free(void *list, void (*f)(void *)) +{ +dane_list head; +dane_list next; + +for(head = (dane_list) list; head; head = next) + { + next = head->next; + if (f && head->value) + f(head->value); + OPENSSL_free(head); + } +} + +static void +dane_mtype_free(void *p) +{ +list_free(((dane_mtype) p)->data, OPENSSL_freeFunc); +OPENSSL_free(p); +} + +static void +dane_selector_free(void *p) +{ +list_free(((dane_selector) p)->mtype, dane_mtype_free); +OPENSSL_free(p); +} + + + +/* + +Tidy up once the connection is finished with. + +Arguments + ssl The ssl connection handle + +=> Before calling SSL_free() +tls_close() and tls_getc() [the error path] are the obvious places. +Could we do it earlier - right after verification? In tls_client_start() +right after SSL_connect() returns, in that case. + +*/ + +void +DANESSL_cleanup(SSL *ssl) +{ +ssl_dane *dane; +int u; + +DEBUG(D_tls) debug_printf("Dane lib-cleanup\n"); + +if(dane_idx < 0 || !(dane = SSL_get_ex_data(ssl, dane_idx))) + return; +(void) SSL_set_ex_data(ssl, dane_idx, 0); + +if(dane->hosts) + list_free(dane->hosts, OPENSSL_freeFunc); +if(dane->mhost) + OPENSSL_free(dane->mhost); +for(u = 0; u <= SSL_DANE_USAGE_LAST; ++u) + if(dane->selectors[u]) + list_free(dane->selectors[u], dane_selector_free); +if(dane->pkeys) + list_free(dane->pkeys, pkey_free); +if(dane->certs) + list_free(dane->certs, cert_free); +if(dane->roots) + sk_X509_pop_free(dane->roots, X509_free); +if(dane->chain) + sk_X509_pop_free(dane->chain, X509_free); +OPENSSL_free(dane); +} + +static dane_host_list +host_list_init(const char **src) +{ +dane_host_list head = NULL; + +while(*src) + { + dane_host_list elem = (dane_host_list) OPENSSL_malloc(sizeof(*elem)); + if(!elem) + { + list_free(head, OPENSSL_freeFunc); + return 0; + } + elem->value = OPENSSL_strdup(*src++); + LINSERT(head, elem); + } +return head; +} + + + + +/* + +Call this for each TLSA record found for the target, after the +DANE setup has been done on the ssl connection handle. + +Arguments: + ssl Connection handle + usage TLSA record field + selector TLSA record field + mdname ??? message digest name? + data ??? TLSA record megalump? + dlen length of data + +Return + -1 on error + 0 action not taken + 1 record accepted +*/ + +int +DANESSL_add_tlsa(SSL *ssl, uint8_t usage, uint8_t selector, const char *mdname, + unsigned const char *data, size_t dlen) +{ +ssl_dane *dane; +dane_selector_list s = 0; +dane_mtype_list m = 0; +dane_data_list d = 0; +dane_cert_list xlist = 0; +dane_pkey_list klist = 0; +const EVP_MD *md = 0; + +DEBUG(D_tls) debug_printf("Dane add-tlsa: usage %u sel %u mdname \"%s\"\n", + usage, selector, mdname); + +if(dane_idx < 0 || !(dane = SSL_get_ex_data(ssl, dane_idx))) + { + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_DANE_INIT); + return -1; + } + +if(usage > SSL_DANE_USAGE_LAST) + { + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_USAGE); + return 0; + } +if(selector > SSL_DANE_SELECTOR_LAST) + { + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_SELECTOR); + return 0; + } +if(mdname && !(md = EVP_get_digestbyname(mdname))) + { + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_DIGEST); + return 0; + } +if(!data) + { + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_NULL_DATA); + return 0; + } +if(mdname && dlen != EVP_MD_size(md)) + { + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_DATA_LENGTH); + return 0; + } + +if(!mdname) + { + X509 *x = 0; + EVP_PKEY *k = 0; + const unsigned char *p = data; + +#define xklistinit(lvar, ltype, var, freeFunc) do { \ + (lvar) = (ltype) OPENSSL_malloc(sizeof(*(lvar))); \ + if (!(lvar)) { \ + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, ERR_R_MALLOC_FAILURE); \ + freeFunc((var)); \ + return 0; \ + } \ + (lvar)->next = 0; \ + lvar->value = var; \ + } while (0) +#define xkfreeret(ret) do { \ + if (xlist) list_free(xlist, cert_free); \ + if (klist) list_free(klist, pkey_free); \ + return (ret); \ + } while (0) + + switch(selector) + { + case SSL_DANE_SELECTOR_CERT: + if(!d2i_X509(&x, &p, dlen) || dlen != p - data) + { + if (x) + X509_free(x); + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_CERT); + return 0; + } + k = X509_get_pubkey(x); + EVP_PKEY_free(k); + if(!k) + { + X509_free(x); + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_CERT_PKEY); + return 0; + } + if(usage == SSL_DANE_USAGE_TRUSTED_CA) + xklistinit(xlist, dane_cert_list, x, X509_free); + break; + + case SSL_DANE_SELECTOR_SPKI: + if(!d2i_PUBKEY(&k, &p, dlen) || dlen != p - data) + { + if(k) + EVP_PKEY_free(k); + DANEerr(DANE_F_SSL_DANE_ADD_TLSA, DANE_R_BAD_PKEY); + return 0; + } + if(usage == SSL_DANE_USAGE_TRUSTED_CA) + xklistinit(klist, dane_pkey_list, k, EVP_PKEY_free); + break; + } + } + +/* Find insertion point and don't add duplicate elements. */ +for(s = dane->selectors[usage]; s; s = s->next) + if(s->value->selector == selector) + for(m = s->value->mtype; m; m = m->next) + if(m->value->md == md) + for(d = m->value->data; d; d = d->next) + if( d->value->datalen == dlen + && memcmp(d->value->data, data, dlen) == 0) + xkfreeret(1); + +if(!(d = (dane_data_list) list_alloc(sizeof(*d->value) + dlen))) + xkfreeret(0); +d->value->datalen = dlen; +memcpy(d->value->data, data, dlen); +if(!m) + { + if(!(m = (dane_mtype_list) list_alloc(sizeof(*m->value)))) + { + list_free(d, OPENSSL_freeFunc); + xkfreeret(0); + } + m->value->data = 0; + if((m->value->md = md) != 0) + m->value->mdlen = dlen; + if(!s) + { + if(!(s = (dane_selector_list) list_alloc(sizeof(*s->value)))) + { + list_free(m, dane_mtype_free); + xkfreeret(0); + } + s->value->mtype = 0; + s->value->selector = selector; + LINSERT(dane->selectors[usage], s); + } + LINSERT(s->value->mtype, m); + } +LINSERT(m->value->data, d); + +if(xlist) + LINSERT(dane->certs, xlist); +else if(klist) + LINSERT(dane->pkeys, klist); +++dane->count; +return 1; +} + + + + +/* +Call this once we have an ssl connection handle but before +making the TLS connection. + +=> In tls_client_start() after the call to SSL_new() +and before the call to SSL_connect(). Exactly where +probably does not matter. +We probably want to keep our existing SNI handling; +call this with NULL. + +Arguments: + ssl Connection handle + sni_domain Optional peer server name + hostnames list of names to chack against peer cert + +Return + -1 on fatal error + 0 nonfatal error + 1 success +*/ + +int +DANESSL_init(SSL *ssl, const char *sni_domain, const char **hostnames) +{ +ssl_dane *dane; +int i; +#ifdef OPENSSL_INTERNAL +SSL_CTX *sctx = SSL_get_SSL_CTX(ssl); + + +if(sctx->app_verify_callback != verify_cert) + { + DANEerr(DANE_F_SSL_DANE_INIT, DANE_R_SCTX_INIT); + return -1; + } +#else +DEBUG(D_tls) debug_printf("Dane ssl-init\n"); +if(dane_idx < 0) + { + DANEerr(DANE_F_SSL_DANE_INIT, DANE_R_LIBRARY_INIT); + return -1; + } +#endif + +if(sni_domain && !SSL_set_tlsext_host_name(ssl, sni_domain)) + return 0; + +if(!(dane = (ssl_dane *) OPENSSL_malloc(sizeof(ssl_dane)))) + { + DANEerr(DANE_F_SSL_DANE_INIT, ERR_R_MALLOC_FAILURE); + return 0; + } +if(!SSL_set_ex_data(ssl, dane_idx, dane)) + { + DANEerr(DANE_F_SSL_DANE_INIT, ERR_R_MALLOC_FAILURE); + OPENSSL_free(dane); + return 0; + } + +dane->verify = 0; +dane->hosts = 0; +dane->thost = 0; +dane->pkeys = 0; +dane->certs = 0; +dane->chain = 0; +dane->roots = 0; +dane->depth = -1; +dane->mhost = 0; /* Future SSL control interface */ +dane->multi = 0; /* Future SSL control interface */ +dane->count = 0; + +for(i = 0; i <= SSL_DANE_USAGE_LAST; ++i) + dane->selectors[i] = 0; + +if(hostnames && !(dane->hosts = host_list_init(hostnames))) + { + DANEerr(DANE_F_SSL_DANE_INIT, ERR_R_MALLOC_FAILURE); + DANESSL_cleanup(ssl); + return 0; + } + +return 1; +} + + +/* + +Call this once we have a context to work with, but +before DANESSL_init() + +=> in tls_client_start(), after tls_init() call gives us the ctx, +if we decide we want to (policy) and can (TLSA records available) +replacing (? what about fallback) everything from testing tls_verify_hosts +down to just before calling SSL_new() for the conn handle. + +Arguments + ctx SSL context + +Return + -1 Error + 1 Success +*/ + +int +DANESSL_CTX_init(SSL_CTX *ctx) +{ +DEBUG(D_tls) debug_printf("Dane ctx-init\n"); +if(dane_idx >= 0) + { + SSL_CTX_set_cert_verify_callback(ctx, verify_cert, 0); + return 1; + } +DANEerr(DANE_F_SSL_CTX_DANE_INIT, DANE_R_LIBRARY_INIT); +return -1; +} + +static int +init_once(volatile int *value, int (*init)(void), void (*postinit)(void)) +{ +int wlock = 0; + +CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); +if(*value < 0) + { + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + wlock = 1; + if(*value < 0) + { + *value = init(); + if(postinit) + postinit(); + } + } +if (wlock) + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); +else + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); +return *value; +} + +static void +dane_init(void) +{ +/* + * Store library id in zeroth function slot, used to locate the library + * name. This must be done before we load the error strings. + */ +#ifndef OPENSSL_NO_ERR +dane_str_functs[0].error |= ERR_PACK(err_lib_dane, 0, 0); +ERR_load_strings(err_lib_dane, dane_str_functs); +ERR_load_strings(err_lib_dane, dane_str_reasons); +#endif + +/* + * Register SHA-2 digests, if implemented and not already registered. + */ +#if defined(LN_sha256) && defined(NID_sha256) && !defined(OPENSSL_NO_SHA256) +if(!EVP_get_digestbyname(LN_sha224)) EVP_add_digest(EVP_sha224()); +if(!EVP_get_digestbyname(LN_sha256)) EVP_add_digest(EVP_sha256()); +#endif +#if defined(LN_sha512) && defined(NID_sha512) && !defined(OPENSSL_NO_SHA512) +if(!EVP_get_digestbyname(LN_sha384)) EVP_add_digest(EVP_sha384()); +if(!EVP_get_digestbyname(LN_sha512)) EVP_add_digest(EVP_sha512()); +#endif + +/* + * Register an SSL index for the connection-specific ssl_dane structure. + * Using a separate index makes it possible to add DANE support to + * existing OpenSSL releases that don't have a suitable pointer in the + * SSL structure. + */ +dane_idx = SSL_get_ex_new_index(0, 0, 0, 0, 0); +} + + + +/* + +Call this once. Probably early in startup will do; may need +to be after SSL library init. + +=> put after call to tls_init() for now + +Return + 1 Success + 0 Fail +*/ + +int +DANESSL_library_init(void) +{ +DEBUG(D_tls) debug_printf("Dane lib-init\n"); +if(err_lib_dane < 0) + init_once(&err_lib_dane, ERR_get_next_error_library, dane_init); + +#if defined(LN_sha256) +/* No DANE without SHA256 support */ +if(dane_idx >= 0 && EVP_get_digestbyname(LN_sha256) != 0) + return 1; +#endif + +DANEerr(DANE_F_SSL_DANE_LIBRARY_INIT, DANE_R_DANE_SUPPORT); +return 0; +} + + +#endif /* OPENSSL_VERSION_NUMBER */ +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/danessl.h exim4-4.86~RC4/src/danessl.h --- exim4-4.84/src/danessl.h 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/danessl.h 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,31 @@ +#ifndef HEADER_SSL_DANE_H +#define HEADER_SSL_DANE_H + +#include +#include + +/*- + * Certificate usages: + * https://tools.ietf.org/html/rfc6698#section-2.1.1 + */ +#define SSL_DANE_USAGE_LIMIT_ISSUER 0 +#define SSL_DANE_USAGE_LIMIT_LEAF 1 +#define SSL_DANE_USAGE_TRUSTED_CA 2 +#define SSL_DANE_USAGE_FIXED_LEAF 3 +#define SSL_DANE_USAGE_LAST SSL_DANE_USAGE_FIXED_LEAF + +/*- + * Selectors: + * https://tools.ietf.org/html/rfc6698#section-2.1.2 + */ +#define SSL_DANE_SELECTOR_CERT 0 +#define SSL_DANE_SELECTOR_SPKI 1 +#define SSL_DANE_SELECTOR_LAST SSL_DANE_SELECTOR_SPKI + +extern int DANESSL_library_init(void); +extern int DANESSL_CTX_init(SSL_CTX *); +extern int DANESSL_init(SSL *, const char *, const char **); +extern void DANESSL_cleanup(SSL *); +extern int DANESSL_add_tlsa(SSL *, uint8_t, uint8_t, const char *, + unsigned const char *, size_t); +#endif diff -Nru exim4-4.84/src/dbfn.c exim4-4.86~RC4/src/dbfn.c --- exim4-4.84/src/dbfn.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/dbfn.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -294,17 +294,21 @@ */ void * -dbfn_read_with_length(open_db *dbblock, uschar *key, int *length) +dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) { void *yield; EXIM_DATUM key_datum, result_datum; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); DEBUG(D_hints_lookup) debug_printf("dbfn_read: key=%s\n", key); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; @@ -334,18 +338,22 @@ */ int -dbfn_write(open_db *dbblock, uschar *key, void *ptr, int length) +dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length) { EXIM_DATUM key_datum, value_datum; dbdata_generic *gptr = (dbdata_generic *)ptr; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); DEBUG(D_hints_lookup) debug_printf("dbfn_write: key=%s\n", key); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; EXIM_DATUM_DATA(value_datum) = CS ptr; EXIM_DATUM_SIZE(value_datum) = length; return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum); @@ -366,12 +374,16 @@ */ int -dbfn_delete(open_db *dbblock, uschar *key) +dbfn_delete(open_db *dbblock, const uschar *key) { +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); EXIM_DATUM key_datum; EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; return EXIM_DBDEL(dbblock->dbptr, key_datum); } diff -Nru exim4-4.84/src/dbfunctions.h exim4-4.86~RC4/src/dbfunctions.h --- exim4-4.84/src/dbfunctions.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/dbfunctions.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,18 +2,18 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading/writing exim database files */ void dbfn_close(open_db *); -int dbfn_delete(open_db *, uschar *); +int dbfn_delete(open_db *, const uschar *); open_db *dbfn_open(uschar *, int, open_db *, BOOL); -void *dbfn_read_with_length(open_db *, uschar *, int *); +void *dbfn_read_with_length(open_db *, const uschar *, int *); uschar *dbfn_scan(open_db *, BOOL, EXIM_CURSOR **); -int dbfn_write(open_db *, uschar *, void *, int); +int dbfn_write(open_db *, const uschar *, void *, int); /* Macro for the common call to read without wanting to know the length. */ diff -Nru exim4-4.84/src/dcc.c exim4-4.86~RC4/src/dcc.c --- exim4-4.84/src/dcc.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/dcc.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Wolfgang Breyha 2005-2013 +/* Copyright (c) Wolfgang Breyha 2005 - 2015 * Vienna University Computer Center * wbreyha@gmx.net * See the file NOTICE for conditions of use and distribution. @@ -45,9 +45,11 @@ return retval; } -int dcc_process(uschar **listptr) { +int +dcc_process(uschar **listptr) +{ int sep = 0; - uschar *list = *listptr; + const uschar *list = *listptr; FILE *data_file; uschar *dcc_default_ip_option = US"127.0.0.1"; uschar *dcc_helo_option = US"localhost"; diff -Nru exim4-4.84/src/debug.c exim4-4.86~RC4/src/debug.c --- exim4-4.84/src/debug.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/debug.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -75,7 +75,7 @@ */ void -debug_print_argv(uschar **argv) +debug_print_argv(const uschar ** argv) { debug_printf("exec"); while (*argv != NULL) debug_printf(" %.256s", *argv++); diff -Nru exim4-4.84/src/deliver.c exim4-4.86~RC4/src/deliver.c --- exim4-4.84/src/deliver.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/deliver.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* The main code for delivering a message. */ @@ -63,10 +63,8 @@ static address_item *addr_remote = NULL; static address_item *addr_route = NULL; static address_item *addr_succeed = NULL; -#ifdef EXPERIMENTAL_DSN static address_item *addr_dsntmp = NULL; static address_item *addr_senddsn = NULL; -#endif static FILE *message_log = NULL; static BOOL update_spool; @@ -129,7 +127,7 @@ { if (addr == NULL) { - uschar ***p = address_expansions; + const uschar ***p = address_expansions; while (*p != NULL) **p++ = NULL; return; } @@ -141,17 +139,19 @@ if (addr->host_list == NULL) { deliver_host = deliver_host_address = US""; + deliver_host_port = 0; } else { deliver_host = addr->host_list->name; deliver_host_address = addr->host_list->address; + deliver_host_port = addr->host_list->port; } deliver_recipients = addr; -deliver_address_data = addr->p.address_data; -deliver_domain_data = addr->p.domain_data; -deliver_localpart_data = addr->p.localpart_data; +deliver_address_data = addr->prop.address_data; +deliver_domain_data = addr->prop.domain_data; +deliver_localpart_data = addr->prop.localpart_data; /* These may be unset for multiple addresses */ @@ -697,7 +697,15 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 && addr->cipher != NULL) s = string_append(s, sizep, ptrp, 2, US" CV=", - testflag(addr, af_cert_verified)? "yes":"no"); + testflag(addr, af_cert_verified) + ? +#ifdef EXPERIMENTAL_DANE + testflag(addr, af_dane_verified) + ? "dane" + : +#endif + "yes" + : "no"); if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL) s = string_append(s, sizep, ptrp, 3, US" DN=\"", string_printing(addr->peerdn), US"\""); @@ -705,6 +713,73 @@ } #endif + + + +#ifdef EXPERIMENTAL_EVENT +uschar * +event_raise(uschar * action, const uschar * event, uschar * ev_data) +{ +uschar * s; +if (action) + { + DEBUG(D_deliver) + debug_printf("Event(%s): event_action=|%s| delivery_IP=%s\n", + event, + action, deliver_host_address); + + event_name = event; + event_data = ev_data; + + if (!(s = expand_string(action)) && *expand_string_message) + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to expand event_action %s in %s: %s\n", + event, transport_name, expand_string_message); + + event_name = event_data = NULL; + + /* If the expansion returns anything but an empty string, flag for + the caller to modify his normal processing + */ + if (s && *s) + { + DEBUG(D_deliver) + debug_printf("Event(%s): event_action returned \"%s\"\n", event, s); + return s; + } + } +return NULL; +} + +static void +msg_event_raise(const uschar * event, const address_item * addr) +{ +const uschar * save_domain = deliver_domain; +uschar * save_local = deliver_localpart; +const uschar * save_host = deliver_host; + +if (!addr->transport) + return; + +router_name = addr->router ? addr->router->name : NULL; +transport_name = addr->transport->name; +deliver_domain = addr->domain; +deliver_localpart = addr->local_part; +deliver_host = addr->host_used ? addr->host_used->name : NULL; + +(void) event_raise(addr->transport->event_action, event, + addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 + ? addr->message : NULL); + +deliver_host = save_host; +deliver_localpart = save_local; +deliver_domain = save_domain; +router_name = transport_name = NULL; +} +#endif /*EXPERIMENTAL_EVENT*/ + + + /* If msg is NULL this is a delivery log and logchar is used. Otherwise this is a nonstandard call; no two-character delivery flag is written but sender-host and sender are prefixed and "msg" is inserted in the log line. @@ -721,19 +796,13 @@ uschar *s; /* building log lines; */ void *reset_point; /* released afterwards. */ - /* Log the delivery on the main log. We use an extensible string to build up the log line, and reset the store afterwards. Remote deliveries should always have a pointer to the host item that succeeded; local deliveries can have a pointer to a single host item in their host list, for use by the transport. */ -#ifdef EXPERIMENTAL_TPDA - tpda_delivery_ip = NULL; /* presume no successful remote delivery */ - tpda_delivery_port = 0; - tpda_delivery_fqdn = NULL; - tpda_delivery_local_part = NULL; - tpda_delivery_domain = NULL; - tpda_delivery_confirmation = NULL; +#ifdef EXPERIMENTAL_EVENT + /* presume no successful remote delivery */ lookup_dnssec_authenticated = NULL; #endif @@ -748,12 +817,23 @@ s = string_append(s, &size, &ptr, 2, US"> ", log_address); } +if (log_extra_selector & LX_incoming_interface && sending_ip_address) + s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]"); + /* for the port: string_sprintf("%d", sending_port) */ + if ((log_extra_selector & LX_sender_on_delivery) != 0 || msg) - s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">"); + s = string_append(s, &size, &ptr, 3, US" F=<", +#ifdef EXPERIMENTAL_INTERNATIONAL + testflag(addr, af_utf8_downcvt) + ? string_address_utf8_to_alabel(sender_address, NULL) + : +#endif + sender_address, + US">"); #ifdef EXPERIMENTAL_SRS -if(addr->p.srs_sender) - s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->p.srs_sender, US">"); +if(addr->prop.srs_sender) + s = string_append(s, &size, &ptr, 3, US" SRS=<", addr->prop.srs_sender, US">"); #endif /* You might think that the return path must always be set for a successful @@ -782,13 +862,8 @@ if (addr->transport->info->local) { - if (addr->host_list != NULL) - { + if (addr->host_list) s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name); - #ifdef EXPERIMENTAL_TPDA - tpda_delivery_fqdn = addr->host_list->name; - #endif - } if (addr->shadow_message != NULL) s = string_cat(s, &size, &ptr, addr->shadow_message, Ustrlen(addr->shadow_message)); @@ -804,24 +879,21 @@ if (continue_sequence > 1) s = string_cat(s, &size, &ptr, US"*", 1); - #ifdef EXPERIMENTAL_TPDA - tpda_delivery_ip = addr->host_used->address; - tpda_delivery_port = addr->host_used->port; - tpda_delivery_fqdn = addr->host_used->name; - tpda_delivery_local_part = addr->local_part; - tpda_delivery_domain = addr->domain; - tpda_delivery_confirmation = addr->message; +#ifdef EXPERIMENTAL_EVENT + deliver_host_address = addr->host_used->address; + deliver_host_port = addr->host_used->port; + deliver_host = addr->host_used->name; /* DNS lookup status */ lookup_dnssec_authenticated = addr->host_used->dnssec==DS_YES ? US"yes" : addr->host_used->dnssec==DS_NO ? US"no" : NULL; - #endif +#endif } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS s = d_tlslog(s, &size, &ptr, addr); - #endif +#endif if (addr->authenticator) { @@ -834,10 +906,10 @@ } } - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (addr->flags & af_prdr_used) s = string_append(s, &size, &ptr, 1, US" PRDR"); - #endif +#endif } /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */ @@ -846,11 +918,12 @@ addr->message && (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0)) { - int i; + unsigned i; + unsigned lim = big_buffer_size < 1024 ? big_buffer_size : 1024; uschar *p = big_buffer; uschar *ss = addr->message; *p++ = '\"'; - for (i = 0; i < 256 && ss[i] != 0; i++) /* limit logged amount */ + for (i = 0; i < lim && ss[i] != 0; i++) /* limit logged amount */ { if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */ *p++ = ss[i]; @@ -876,22 +949,10 @@ s[ptr] = 0; log_write(0, flags, "%s", s); -#ifdef EXPERIMENTAL_TPDA -if (addr->transport->tpda_delivery_action) - { - DEBUG(D_deliver) - debug_printf(" TPDA(Delivery): tpda_deliver_action=|%s| tpda_delivery_IP=%s\n", - addr->transport->tpda_delivery_action, tpda_delivery_ip); - - router_name = addr->router->name; - transport_name = addr->transport->name; - if (!expand_string(addr->transport->tpda_delivery_action) && *expand_string_message) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_deliver_action in %s: %s\n", - transport_name, expand_string_message); - router_name = NULL; - transport_name = NULL; - } +#ifdef EXPERIMENTAL_EVENT +if (!msg) msg_event_raise(US"msg:delivery", addr); #endif + store_reset(reset_point); return; } @@ -930,7 +991,6 @@ uschar *s; /* building log lines; */ void *reset_point; /* released afterwards. */ - DEBUG(D_deliver) debug_printf("post-process %s (%d)\n", addr->address, result); /* Set up driver kind and name for logging. Disable logging if the router or @@ -966,7 +1026,10 @@ if (addr->message != NULL) { - addr->message = string_printing(addr->message); + const uschar * s = string_printing(addr->message); + if (s != addr->message) + addr->message = US s; + /* deconst cast ok as string_printing known to have alloc'n'copied */ if (((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) && (Ustrstr(addr->message, "mysql") != NULL || Ustrstr(addr->message, "pgsql") != NULL || @@ -1023,11 +1086,12 @@ if (s != NULL) { uschar *p = big_buffer + Ustrlen(big_buffer); + const uschar * sp; while (p > big_buffer && isspace(p[-1])) p--; *p = 0; - s = string_printing(big_buffer); + sp = string_printing(big_buffer); log_write(0, LOG_MAIN, "<%s>: %s transport output: %s", - addr->address, tb->name, s); + addr->address, tb->name, sp); } (void)fclose(f); } @@ -1036,7 +1100,7 @@ /* Handle returning options, but only if there is an address to return the text to. */ - if (sender_address[0] != 0 || addr->p.errors_address != NULL) + if (sender_address[0] != 0 || addr->prop.errors_address != NULL) { if (tb->return_output) { @@ -1088,8 +1152,8 @@ child_done(addr, now); } - /* Certificates for logging (via TPDA) */ - #ifdef SUPPORT_TLS + /* Certificates for logging (via events) */ +#ifdef SUPPORT_TLS tls_out.ourcert = addr->ourcert; addr->ourcert = NULL; tls_out.peercert = addr->peercert; @@ -1098,25 +1162,23 @@ tls_out.cipher = addr->cipher; tls_out.peerdn = addr->peerdn; tls_out.ocsp = addr->ocsp; - #endif +# ifdef EXPERIMENTAL_DANE + tls_out.dane_verified = testflag(addr, af_dane_verified); +# endif +#endif delivery_log(LOG_MAIN, addr, logchar, NULL); - #ifdef SUPPORT_TLS - if (tls_out.ourcert) - { - tls_free_cert(tls_out.ourcert); - tls_out.ourcert = NULL; - } - if (tls_out.peercert) - { - tls_free_cert(tls_out.peercert); - tls_out.peercert = NULL; - } +#ifdef SUPPORT_TLS + tls_free_cert(&tls_out.ourcert); + tls_free_cert(&tls_out.peercert); tls_out.cipher = NULL; tls_out.peerdn = NULL; tls_out.ocsp = OCSP_NOT_REQ; - #endif +# ifdef EXPERIMENTAL_DANE + tls_out.dane_verified = FALSE; +# endif +#endif } @@ -1200,6 +1262,11 @@ s = string_append(s, &size, &ptr, 2, US": ", US strerror(addr->basic_errno)); + if (addr->host_used) + s = string_append(s, &size, &ptr, 5, + US" H=", addr->host_used->name, + US" [", addr->host_used->address, US"]"); + if (addr->message != NULL) s = string_append(s, &size, &ptr, 2, US": ", addr->message); @@ -1242,7 +1309,7 @@ if (!testflag(addr, af_ignore_error) && (addr->special_action == SPECIAL_FREEZE || - (sender_address[0] == 0 && addr->p.errors_address == NULL) + (sender_address[0] == 0 && addr->prop.errors_address == NULL) )) { frozen_info = (addr->special_action == SPECIAL_FREEZE)? US"" : @@ -1297,9 +1364,9 @@ if (addr->host_used != NULL) s = d_hostlog(s, &size, &ptr, addr); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS s = d_tlslog(s, &size, &ptr, addr); - #endif +#endif if (addr->basic_errno > 0) s = string_append(s, &size, &ptr, 2, US": ", @@ -1319,6 +1386,11 @@ deliver_msglog("%s %s\n", now, s); log_write(0, LOG_MAIN, "** %s", s); + +#ifdef EXPERIMENTAL_EVENT + msg_event_raise(US"msg:fail:delivery", addr); +#endif + store_reset(reset_point); } @@ -1737,11 +1809,11 @@ /* Set up the return path from the errors or sender address. If the transport has its own return path setting, expand it and replace the existing value. */ -if(addr->p.errors_address != NULL) - return_path = addr->p.errors_address; +if(addr->prop.errors_address != NULL) + return_path = addr->prop.errors_address; #ifdef EXPERIMENTAL_SRS -else if(addr->p.srs_sender != NULL) - return_path = addr->p.srs_sender; +else if(addr->prop.srs_sender != NULL) + return_path = addr->prop.srs_sender; #endif else return_path = sender_address; @@ -1888,19 +1960,19 @@ diagnosis that it's reasonable to make them something that has to be explicitly requested. */ - #ifdef RLIMIT_CORE +#ifdef RLIMIT_CORE struct rlimit rl; rl.rlim_cur = 0; rl.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rl) < 0) { - #ifdef SETRLIMIT_NOT_SUPPORTED +# ifdef SETRLIMIT_NOT_SUPPORTED if (errno != ENOSYS && errno != ENOTSUP) - #endif +# endif log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_CORE) failed: %s", strerror(errno)); } - #endif +#endif /* Reset the random number generator, so different processes don't all have the same sequence. */ @@ -2353,9 +2425,9 @@ (addr->flags & (af_pfr|af_file)) == (next->flags & (af_pfr|af_file)) && (!uses_lp || Ustrcmp(next->local_part, addr->local_part) == 0) && (!uses_dom || Ustrcmp(next->domain, addr->domain) == 0) && - same_strings(next->p.errors_address, addr->p.errors_address) && - same_headers(next->p.extra_headers, addr->p.extra_headers) && - same_strings(next->p.remove_headers, addr->p.remove_headers) && + same_strings(next->prop.errors_address, addr->prop.errors_address) && + same_headers(next->prop.extra_headers, addr->prop.extra_headers) && + same_strings(next->prop.remove_headers, addr->prop.remove_headers) && same_ugid(tp, addr, next) && ((addr->host_list == NULL && next->host_list == NULL) || (addr->host_list != NULL && next->host_list != NULL && @@ -2711,7 +2783,7 @@ { int sep = 0; address_item **aptr = &addr_remote; -uschar *listptr = remote_sort_domains; +const uschar *listptr = remote_sort_domains; uschar *pattern; uschar patbuf[256]; @@ -2726,7 +2798,7 @@ { address_item **next; deliver_domain = (*aptr)->domain; /* set $domain */ - if (match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1, + if (match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK) { aptr = &((*aptr)->next); @@ -2736,7 +2808,7 @@ next = &((*aptr)->next); while (*next != NULL && (deliver_domain = (*next)->domain, /* Set $domain */ - match_isinlist(deliver_domain, &pattern, UCHAR_MAX+1, + match_isinlist(deliver_domain, (const uschar **)&pattern, UCHAR_MAX+1, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) != OK) next = &((*next)->next); @@ -2823,6 +2895,8 @@ uschar *msg = p->msg; BOOL done = p->done; BOOL unfinished = TRUE; +/* minimum size to read is header size including id, subid and length */ +int required = PIPE_HEADER_SIZE; /* Loop through all items, reading from the pipe when necessary. The pipe is set up to be non-blocking, but there are two different Unix mechanisms in @@ -2845,12 +2919,15 @@ { retry_item *r, **rp; int remaining = endptr - ptr; + uschar header[PIPE_HEADER_SIZE + 1]; + uschar id, subid; + uschar *endc; /* Read (first time) or top up the chars in the buffer if necessary. There will be only one read if we get all the available data (i.e. don't fill the buffer completely). */ - if (remaining < 2500 && unfinished) + if (remaining < required && unfinished) { int len; int available = big_buffer_size - remaining; @@ -2883,17 +2960,63 @@ won't read any more, as "unfinished" will get set FALSE. */ endptr += len; + remaining += len; unfinished = len == available; } /* If we are at the end of the available data, exit the loop. */ - if (ptr >= endptr) break; + /* copy and read header */ + memcpy(header, ptr, PIPE_HEADER_SIZE); + header[PIPE_HEADER_SIZE] = '\0'; + id = header[0]; + subid = header[1]; + required = Ustrtol(header + 2, &endc, 10) + PIPE_HEADER_SIZE; /* header + data */ + if (*endc) + { + msg = string_sprintf("failed to read pipe from transport process " + "%d for transport %s: error reading size from header", pid, addr->transport->driver_name); + done = TRUE; + break; + } + + DEBUG(D_deliver) + debug_printf("header read id:%c,subid:%c,size:%s,required:%d,remaining:%d,unfinished:%d\n", + id, subid, header+2, required, remaining, unfinished); + + /* is there room for the dataset we want to read ? */ + if (required > big_buffer_size - PIPE_HEADER_SIZE) + { + msg = string_sprintf("failed to read pipe from transport process " + "%d for transport %s: big_buffer too small! required size=%d buffer size=%d", pid, addr->transport->driver_name, + required, big_buffer_size - PIPE_HEADER_SIZE); + done = TRUE; + break; + } + + /* we wrote all datasets with atomic write() calls + remaining < required only happens if big_buffer was too small + to get all available data from pipe. unfinished has to be true + as well. */ + if (remaining < required) + { + if (unfinished) + continue; + msg = string_sprintf("failed to read pipe from transport process " + "%d for transport %s: required size=%d > remaining size=%d and unfinished=false", + pid, addr->transport->driver_name, required, remaining); + done = TRUE; + break; + } + + /* step behind the header */ + ptr += PIPE_HEADER_SIZE; + /* Handle each possible type of item, assuming the complete item is available in store. */ - switch (*ptr++) + switch (id) { /* Host items exist only if any hosts were marked unusable. Match up by checking the IP address. */ @@ -2987,10 +3110,10 @@ it in with the other info, in order to keep each message short enough to guarantee it won't be split in the pipe. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS case 'X': if (addr == NULL) goto ADDR_MISMATCH; /* Below, in 'A' handler */ - switch (*ptr++) + switch (subid) { case '1': addr->cipher = NULL; @@ -3004,31 +3127,33 @@ break; case '2': - addr->peercert = NULL; if (*ptr) (void) tls_import_cert(ptr, &addr->peercert); + else + addr->peercert = NULL; break; case '3': - addr->ourcert = NULL; if (*ptr) (void) tls_import_cert(ptr, &addr->ourcert); + else + addr->ourcert = NULL; break; - #ifndef DISABLE_OCSP +# ifndef DISABLE_OCSP case '4': addr->ocsp = OCSP_NOT_REQ; if (*ptr) addr->ocsp = *ptr - '0'; break; - #endif +# endif } while (*ptr++); break; - #endif /*SUPPORT_TLS*/ +#endif /*SUPPORT_TLS*/ case 'C': /* client authenticator information */ - switch (*ptr++) + switch (subid) { case '1': addr->authenticator = (*ptr)? string_copy(ptr) : NULL; @@ -3049,14 +3174,12 @@ break; #endif - #ifdef EXPERIMENTAL_DSN case 'D': - if (addr == NULL) break; + if (addr == NULL) goto ADDR_MISMATCH; memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); ptr += sizeof(addr->dsn_aware); DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); break; - #endif case 'A': if (addr == NULL) @@ -3106,6 +3229,14 @@ addr = addr->next; break; + /* Local interface address/port */ + case 'I': + if (*ptr) sending_ip_address = string_copy(ptr); + while (*ptr++) ; + if (*ptr) sending_port = atoi(CS ptr); + while (*ptr++) ; + break; + /* Z marks the logical end of the data. It is followed by '0' if continue_transport was NULL at the end of transporting, otherwise '1'. We need to know when it becomes NULL during a delivery down a passed SMTP @@ -3119,7 +3250,7 @@ continue_hostname = NULL; } done = TRUE; - DEBUG(D_deliver) debug_printf("Z%c item read\n", *ptr); + DEBUG(D_deliver) debug_printf("Z0%c item read\n", *ptr); break; /* Anything else is a disaster. */ @@ -3572,9 +3703,40 @@ static void -rmt_dlv_checked_write(int fd, void * buf, int size) +rmt_dlv_checked_write(int fd, char id, char subid, void * buf, int size) +{ +uschar writebuffer[PIPE_HEADER_SIZE + BIG_BUFFER_SIZE]; +int header_length; + +/* we assume that size can't get larger then BIG_BUFFER_SIZE which currently is set to 16k */ +/* complain to log if someone tries with buffer sizes we can't handle*/ + +if (size > 99999) { -int ret = write(fd, buf, size); + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "Failed writing transport result to pipe: can't handle buffers > 99999 bytes. truncating!\n"); + size = 99999; +} + +/* to keep the write() atomic we build header in writebuffer and copy buf behind */ +/* two write() calls would increase the complexity of reading from pipe */ + +/* convert size to human readable string prepended by id and subid */ +header_length = snprintf(CS writebuffer, PIPE_HEADER_SIZE+1, "%c%c%05d", id, subid, size); +if (header_length != PIPE_HEADER_SIZE) +{ + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "header snprintf failed\n"); + writebuffer[0] = '\0'; +} + +DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%d,final:%s\n", + id, subid, size, writebuffer); + +if (buf && size > 0) + memcpy(writebuffer + PIPE_HEADER_SIZE, buf, size); + +size += PIPE_HEADER_SIZE; +int ret = write(fd, writebuffer, size); if(ret != size) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed writing transport result to pipe: %s\n", ret == -1 ? strerror(errno) : "short write"); @@ -3696,9 +3858,20 @@ } /* Get the flag which specifies whether the transport can handle different - domains that nevertheless resolve to the same set of hosts. */ - - multi_domain = tp->multi_domain; + domains that nevertheless resolve to the same set of hosts. If it needs + expanding, get variables set: $address_data, $domain_data, $localpart_data, + $host, $host_address, $host_port. */ + if (tp->expand_multi_domain) + deliver_set_expansions(addr); + + if (exp_bool(addr, US"transport", tp->name, D_transport, + US"multi_domain", tp->multi_domain, tp->expand_multi_domain, + &multi_domain) != OK) + { + deliver_set_expansions(NULL); + remote_post_process(addr, LOG_MAIN|LOG_PANIC, addr->message, fallback); + continue; + } /* Get the maximum it can handle in one envelope, with zero meaning unlimited, which is forced for the MUA wrapper case. */ @@ -3767,26 +3940,35 @@ entirely different domains. The host list pointers can be NULL in the case where the hosts are defined in the transport. There is also a configured maximum limit of addresses that can be handled at once (see comments above - for how it is computed). */ + for how it is computed). + If the transport does not handle multiple domains, enforce that also, + and if it might need a per-address check for this, re-evaluate it. + */ while ((next = *anchor) != NULL && address_count < address_count_max) { - if ((multi_domain || Ustrcmp(next->domain, addr->domain) == 0) - && - tp == next->transport - && - same_hosts(next->host_list, addr->host_list) - && - same_strings(next->p.errors_address, addr->p.errors_address) - && - same_headers(next->p.extra_headers, addr->p.extra_headers) - && - same_ugid(tp, next, addr) - && - (next->p.remove_headers == addr->p.remove_headers || - (next->p.remove_headers != NULL && - addr->p.remove_headers != NULL && - Ustrcmp(next->p.remove_headers, addr->p.remove_headers) == 0))) + BOOL md; + if ( (multi_domain || Ustrcmp(next->domain, addr->domain) == 0) + && tp == next->transport + && same_hosts(next->host_list, addr->host_list) + && same_strings(next->prop.errors_address, addr->prop.errors_address) + && same_headers(next->prop.extra_headers, addr->prop.extra_headers) + && same_ugid(tp, next, addr) + && ( next->prop.remove_headers == addr->prop.remove_headers + || ( next->prop.remove_headers + && addr->prop.remove_headers + && Ustrcmp(next->prop.remove_headers, addr->prop.remove_headers) == 0 + ) ) + && ( !multi_domain + || ( ( + !tp->expand_multi_domain || (deliver_set_expansions(next), 1), + exp_bool(addr, + US"transport", next->transport->name, D_transport, + US"multi_domain", next->transport->multi_domain, + next->transport->expand_multi_domain, &md) == OK + ) + && md + ) ) ) { *anchor = next->next; next->next = NULL; @@ -3796,6 +3978,7 @@ address_count++; } else anchor = &(next->next); + deliver_set_expansions(NULL); } /* If we are acting as an MUA wrapper, all addresses must go in a single @@ -3818,11 +4001,11 @@ /* Compute the return path, expanding a new one if required. The old one must be set first, as it might be referred to in the expansion. */ - if(addr->p.errors_address != NULL) - return_path = addr->p.errors_address; + if(addr->prop.errors_address != NULL) + return_path = addr->prop.errors_address; #ifdef EXPERIMENTAL_SRS - else if(addr->p.srs_sender != NULL) - return_path = addr->p.srs_sender; + else if(addr->prop.srs_sender != NULL) + return_path = addr->prop.srs_sender; #endif else return_path = sender_address; @@ -3954,11 +4137,11 @@ that it can use either of them, though it prefers O_NONBLOCK, which distinguishes between EOF and no-more-data. */ - #ifdef O_NONBLOCK +#ifdef O_NONBLOCK (void)fcntl(pfd[pipe_read], F_SETFL, O_NONBLOCK); - #else +#else (void)fcntl(pfd[pipe_read], F_SETFL, O_NDELAY); - #endif +#endif /* If the maximum number of subprocesses already exist, wait for a process to finish. If we ran out of file descriptors, parmax will have been reduced @@ -4100,8 +4283,8 @@ for (h = addr->host_list; h != NULL; h = h->next) { if (h->address == NULL || h->status < hstatus_unusable) continue; - sprintf(CS big_buffer, "H%c%c%s", h->status, h->why, h->address); - rmt_dlv_checked_write(fd, big_buffer, Ustrlen(big_buffer+3) + 4); + sprintf(CS big_buffer, "%c%c%s", h->status, h->why, h->address); + rmt_dlv_checked_write(fd, 'H', '0', big_buffer, Ustrlen(big_buffer+2) + 3); } /* The number of bytes written. This is the same for each address. Even @@ -4109,9 +4292,8 @@ size of each one is the same, and it's that value we have got because transport_count gets reset before calling transport_write_message(). */ - big_buffer[0] = 'S'; - memcpy(big_buffer+1, &transport_count, sizeof(transport_count)); - rmt_dlv_checked_write(fd, big_buffer, sizeof(transport_count) + 1); + memcpy(big_buffer, &transport_count, sizeof(transport_count)); + rmt_dlv_checked_write(fd, 'S', '0', big_buffer, sizeof(transport_count)); /* Information about what happened to each address. Four item types are used: an optional 'X' item first, for TLS information, then an optional "C" @@ -4125,13 +4307,16 @@ /* The certificate verification status goes into the flags */ if (tls_out.certificate_verified) setflag(addr, af_cert_verified); +#ifdef EXPERIMENTAL_DANE + if (tls_out.dane_verified) setflag(addr, af_dane_verified); +#endif /* Use an X item only if there's something to send */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (addr->cipher) { ptr = big_buffer; - sprintf(CS ptr, "X1%.128s", addr->cipher); + sprintf(CS ptr, "%.128s", addr->cipher); while(*ptr++); if (!addr->peerdn) *ptr++ = 0; @@ -4141,79 +4326,74 @@ while(*ptr++); } - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer); } if (addr->peercert) { ptr = big_buffer; - *ptr++ = 'X'; *ptr++ = '2'; if (!tls_export_cert(ptr, big_buffer_size-2, addr->peercert)) while(*ptr++); else *ptr++ = 0; - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'X', '2', big_buffer, ptr - big_buffer); } if (addr->ourcert) { ptr = big_buffer; - *ptr++ = 'X'; *ptr++ = '3'; if (!tls_export_cert(ptr, big_buffer_size-2, addr->ourcert)) while(*ptr++); else *ptr++ = 0; - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'X', '3', big_buffer, ptr - big_buffer); } - #ifndef DISABLE_OCSP +# ifndef DISABLE_OCSP if (addr->ocsp > OCSP_NOT_REQ) { ptr = big_buffer; - sprintf(CS ptr, "X4%c", addr->ocsp + '0'); + sprintf(CS ptr, "%c", addr->ocsp + '0'); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer); } - # endif - #endif /*SUPPORT_TLS*/ +# endif +#endif /*SUPPORT_TLS*/ if (client_authenticator) { ptr = big_buffer; - sprintf(CS big_buffer, "C1%.64s", client_authenticator); + sprintf(CS big_buffer, "%.64s", client_authenticator); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'C', '1', big_buffer, ptr - big_buffer); } if (client_authenticated_id) { ptr = big_buffer; - sprintf(CS big_buffer, "C2%.64s", client_authenticated_id); + sprintf(CS big_buffer, "%.64s", client_authenticated_id); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'C', '2', big_buffer, ptr - big_buffer); } if (client_authenticated_sender) { ptr = big_buffer; - sprintf(CS big_buffer, "C3%.64s", client_authenticated_sender); + sprintf(CS big_buffer, "%.64s", client_authenticated_sender); while(*ptr++); - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'C', '3', big_buffer, ptr - big_buffer); } - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR if (addr->flags & af_prdr_used) - rmt_dlv_checked_write(fd, "P", 1); - #endif + rmt_dlv_checked_write(fd, 'P', '0', NULL, 0); +#endif - #ifdef EXPERIMENTAL_DSN - big_buffer[0] = 'D'; - memcpy(big_buffer+1, &addr->dsn_aware, sizeof(addr->dsn_aware)); - rmt_dlv_checked_write(fd, big_buffer, sizeof(addr->dsn_aware) + 1); + memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware)); + rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware)); DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware); - #endif /* Retry information: for most success cases this will be null. */ for (r = addr->retries; r != NULL; r = r->next) { uschar *ptr; - sprintf(CS big_buffer, "R%c%.500s", r->flags, r->key); + sprintf(CS big_buffer, "%c%.500s", r->flags, r->key); ptr = big_buffer + Ustrlen(big_buffer+2) + 3; memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno)); ptr += sizeof(r->basic_errno); @@ -4224,13 +4404,13 @@ sprintf(CS ptr, "%.512s", r->message); while(*ptr++); } - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer); } /* The rest of the information goes in an 'A' item. */ - ptr = big_buffer + 3; - sprintf(CS big_buffer, "A%c%c", addr->transport_return, + ptr = big_buffer + 2; + sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action); memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno)); ptr += sizeof(addr->basic_errno); @@ -4265,7 +4445,19 @@ : addr->host_used->dnssec==DS_NO ? '1' : '0'; } - rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer); + rmt_dlv_checked_write(fd, 'A', '0', big_buffer, ptr - big_buffer); + } + + /* Local interface address/port */ + if (log_extra_selector & LX_incoming_interface && sending_ip_address) + { + uschar * ptr = big_buffer; + sprintf(CS ptr, "%.128s", sending_ip_address); + while(*ptr++); + sprintf(CS ptr, "%d", sending_port); + while(*ptr++); + + rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer); } /* Add termination flag, close the pipe, and that's it. The character @@ -4273,9 +4465,8 @@ A change from non-NULL to NULL indicates a problem with a continuing connection. */ - big_buffer[0] = 'Z'; - big_buffer[1] = (continue_transport == NULL)? '0' : '1'; - rmt_dlv_checked_write(fd, big_buffer, 2); + big_buffer[0] = (continue_transport == NULL)? '0' : '1'; + rmt_dlv_checked_write(fd, 'Z', '0', big_buffer, 1); (void)close(fd); exit(EXIT_SUCCESS); } @@ -4395,7 +4586,7 @@ deliver_domain = addr->domain; /* set $domain */ - while ((rc = match_isinlist(deliver_domain, &percent_hack_domains, 0, + while ((rc = match_isinlist(deliver_domain, (const uschar **)&percent_hack_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) == OK && (t = Ustrrchr(local_part, '%')) != NULL) @@ -4577,6 +4768,10 @@ string_printing(original)); } +if (addr->host_used) + fprintf(f, "\n host %s [%s]", + addr->host_used->name, addr->host_used->address); + fprintf(f, "%s", CS se); return yield; } @@ -4614,15 +4809,12 @@ int count = Ustrlen(t); uschar *s = testflag(addr, af_pass_message)? addr->message : NULL; -if (s == NULL) - { - if (addr->user_message != NULL) s = addr->user_message; else return; - } +if (!s && !(s = addr->user_message)) + return; fprintf(f, "\n %s", t); -while (*s != 0) - { +while (*s) if (*s == '\\' && s[1] == 'n') { fprintf(f, "\n "); @@ -4639,12 +4831,59 @@ count = 0; } } - } } +/*********************************************************** +* Print Diagnostic-Code for an address * +************************************************************/ + +/* This function is called to print the error information out of an address for +a bounce or a warning message. It tries to format the message reasonably as +required by RFC 3461 by adding a space after each newline + +it uses the same logic as print_address_error() above. if af_pass_message is true +and addr->message is set it uses the remote host answer. if not addr->user_message +is used instead if available. + +Arguments: + addr the address + f the FILE to print on + +Returns: nothing +*/ + +static void +print_dsn_diagnostic_code(const address_item *addr, FILE *f) +{ +uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL; + +/* af_pass_message and addr->message set ? print remote host answer */ +if (s) + { + DEBUG(D_deliver) + debug_printf("DSN Diagnostic-Code: addr->message = %s\n", addr->message); + + /* search first ": ". we assume to find the remote-MTA answer there */ + if (!(s = Ustrstr(addr->message, ": "))) + return; /* not found, bail out */ + s += 2; /* skip ": " */ + fprintf(f, "Diagnostic-Code: smtp; "); + } +/* no message available. do nothing */ +else return; +while (*s) + if (*s == '\\' && s[1] == 'n') + { + fputs("\n ", f); /* as defined in RFC 3461 */ + s += 2; + } + else + fputc(*s++, f); +fputc('\n', f); +} /************************************************* @@ -4921,7 +5160,7 @@ if (deliver_freeze) { - #ifdef SUPPORT_MOVE_FROZEN_MESSAGES +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES /* Moving to another directory removes the message from Exim's view. Other tools must be used to deal with it. Logging of this action happens in spool_move_message() and its subfunctions. */ @@ -4929,7 +5168,7 @@ if (move_frozen_messages && spool_move_message(id, message_subdir, US"", US"F")) return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ - #endif +#endif /* For all frozen messages (bounces or not), timeout_frozen_after sets the maximum time to keep messages that are frozen. Thaw if we reach it, with a @@ -5353,18 +5592,28 @@ { recipient_item *r = recipients_list + i; address_item *new = deliver_make_addr(r->address, FALSE); - new->p.errors_address = r->errors_to; + new->prop.errors_address = r->errors_to; +#ifdef EXPERIMENTAL_INTERNATIONAL + if ((new->prop.utf8_msg = message_smtputf8)) + { + new->prop.utf8_downcvt = message_utf8_downconvert == 1; + new->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1; + DEBUG(D_deliver) debug_printf("utf8, downconvert %s\n", + new->prop.utf8_downcvt ? "yes" + : new->prop.utf8_downcvt_maybe ? "ifneeded" + : "no"); + } +#endif if (r->pno >= 0) new->onetime_parent = recipients_list[r->pno].address; - #ifdef EXPERIMENTAL_DSN /* If DSN support is enabled, set the dsn flags and the original receipt to be passed on to other DSN enabled MTAs */ new->dsn_flags = r->dsn_flags & rf_dsnflags; new->dsn_orcpt = r->orcpt; - DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", new->dsn_orcpt, new->dsn_flags); - #endif + DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: %d\n", + new->dsn_orcpt, new->dsn_flags); switch (process_recipients) { @@ -5434,6 +5683,25 @@ addr_last = new; break; } + +#ifdef EXPERIMENTAL_EVENT + if (process_recipients != RECIP_ACCEPT) + { + uschar * save_local = deliver_localpart; + const uschar * save_domain = deliver_domain; + + deliver_localpart = expand_string( + string_sprintf("${local_part:%s}", new->address)); + deliver_domain = expand_string( + string_sprintf("${domain:%s}", new->address)); + + (void) event_raise(event_action, + US"msg:fail:internal", new->message); + + deliver_localpart = save_local; + deliver_domain = save_domain; + } +#endif } } } @@ -5685,7 +5953,7 @@ deliver_domain = addr->domain; /* set $domain */ if (!forced && hold_domains != NULL && - (rc = match_isinlist(addr->domain, &hold_domains, 0, + (rc = match_isinlist(addr->domain, (const uschar **)&hold_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) != FAIL) { @@ -5892,7 +6160,7 @@ addr_route = addr->next; deliver_domain = addr->domain; /* set $domain */ - if ((rc = match_isinlist(addr->domain, &queue_domains, 0, + if ((rc = match_isinlist(addr->domain, (const uschar **)&queue_domains, 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) != OK) { @@ -5925,15 +6193,15 @@ { int rc; address_item *addr = addr_route; - uschar *old_domain = addr->domain; + const uschar *old_domain = addr->domain; uschar *old_unique = addr->unique; addr_route = addr->next; addr->next = NULL; /* Just in case some router parameter refers to it. */ - return_path = (addr->p.errors_address != NULL)? - addr->p.errors_address : sender_address; + return_path = (addr->prop.errors_address != NULL)? + addr->prop.errors_address : sender_address; /* If a router defers an address, add a retry item. Whether or not to use the local part in the key is a property of the router. */ @@ -6003,8 +6271,8 @@ if (addr_remote == addr && addr->router->same_domain_copy_routing && - addr->p.extra_headers == NULL && - addr->p.remove_headers == NULL && + addr->prop.extra_headers == NULL && + addr->prop.remove_headers == NULL && old_domain == addr->domain) { address_item **chain = &addr_route; @@ -6031,7 +6299,7 @@ addr2->transport = addr->transport; addr2->host_list = addr->host_list; addr2->fallback_hosts = addr->fallback_hosts; - addr2->p.errors_address = addr->p.errors_address; + addr2->prop.errors_address = addr->prop.errors_address; copyflag(addr2, addr, af_hide_child | af_local_host_removed); DEBUG(D_deliver|D_route) @@ -6290,31 +6558,7 @@ /* Precompile some regex that are used to recognize parameters in response to an EHLO command, if they aren't already compiled. */ - if (regex_PIPELINING == NULL) regex_PIPELINING = - regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE); - - if (regex_SIZE == NULL) regex_SIZE = - regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE); - - if (regex_AUTH == NULL) regex_AUTH = - regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", - FALSE, TRUE); - - #ifdef SUPPORT_TLS - if (regex_STARTTLS == NULL) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); - #endif - - #ifndef DISABLE_PRDR - if (regex_PRDR == NULL) regex_PRDR = - regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); - #endif - - #ifdef EXPERIMENTAL_DSN - /* Set the regex to check for DSN support on remote MTA */ - if (regex_DSN == NULL) regex_DSN = - regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); - #endif + deliver_init(); /* Now sort the addresses if required, and do the deliveries. The yield of do_remote_deliveries is FALSE when mua_wrapper is set and all addresses @@ -6390,6 +6634,7 @@ { uschar *s = (addr_failed->user_message != NULL)? addr_failed->user_message : addr_failed->message; + host_item * host; fprintf(stderr, "Delivery failed: "); if (addr_failed->basic_errno > 0) @@ -6397,6 +6642,8 @@ fprintf(stderr, "%s", strerror(addr_failed->basic_errno)); if (s != NULL) fprintf(stderr, ": "); } + if ((host = addr_failed->host_used)) + fprintf(stderr, "H=%s [%s]: ", host->name, host->address); if (s == NULL) { if (addr_failed->basic_errno <= 0) fprintf(stderr, "unknown error"); @@ -6420,34 +6667,40 @@ else if (!dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); -#ifdef EXPERIMENTAL_DSN /* Send DSN for successful messages */ addr_dsntmp = addr_succeed; addr_senddsn = NULL; -while(addr_dsntmp != NULL) +while(addr_dsntmp) { - DEBUG(D_deliver) - debug_printf("DSN: processing router : %s\n", addr_dsntmp->router->name); - - DEBUG(D_deliver) - debug_printf("DSN: processing successful delivery address: %s\n", addr_dsntmp->address); - /* af_ignore_error not honored here. it's not an error */ - - DEBUG(D_deliver) debug_printf("DSN: Sender_address: %s\n", sender_address); - DEBUG(D_deliver) debug_printf("DSN: orcpt: %s flags: %d\n", addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags); - DEBUG(D_deliver) debug_printf("DSN: envid: %s ret: %d\n", dsn_envid, dsn_ret); - DEBUG(D_deliver) debug_printf("DSN: Final recipient: %s\n", addr_dsntmp->address); - DEBUG(D_deliver) debug_printf("DSN: Remote SMTP server supports DSN: %d\n", addr_dsntmp->dsn_aware); + DEBUG(D_deliver) + { + debug_printf("DSN: processing router : %s\n" + "DSN: processing successful delivery address: %s\n" + "DSN: Sender_address: %s\n" + "DSN: orcpt: %s flags: %d\n" + "DSN: envid: %s ret: %d\n" + "DSN: Final recipient: %s\n" + "DSN: Remote SMTP server supports DSN: %d\n", + addr_dsntmp->router->name, + addr_dsntmp->address, + sender_address, + addr_dsntmp->dsn_orcpt, addr_dsntmp->dsn_flags, + dsn_envid, dsn_ret, + addr_dsntmp->address, + addr_dsntmp->dsn_aware + ); + } /* send report if next hop not DSN aware or a router flagged "last DSN hop" and a report was requested */ - if (((addr_dsntmp->dsn_aware != dsn_support_yes) || - ((addr_dsntmp->dsn_flags & rf_dsnlasthop) != 0)) - && - (((addr_dsntmp->dsn_flags & rf_dsnflags) != 0) && - ((addr_dsntmp->dsn_flags & rf_notify_success) != 0))) + if ( ( addr_dsntmp->dsn_aware != dsn_support_yes + || addr_dsntmp->dsn_flags & rf_dsnlasthop + ) + && addr_dsntmp->dsn_flags & rf_dsnflags + && addr_dsntmp->dsn_flags & rf_notify_success + ) { /* copy and relink address_item and send report with all of them at once later */ address_item *addr_next; @@ -6457,14 +6710,12 @@ addr_senddsn->next = addr_next; } else - { - DEBUG(D_deliver) debug_printf("DSN: *** NOT SENDING DSN SUCCESS Message ***\n"); - } + DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); addr_dsntmp = addr_dsntmp->next; } -if (addr_senddsn != NULL) +if (addr_senddsn) { pid_t pid; int fd; @@ -6480,22 +6731,21 @@ "create child process to send failure message: %s", getpid(), getppid(), strerror(errno)); - DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); - + DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); } else /* Creation of child succeeded */ { FILE *f = fdopen(fd, "wb"); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ int topt = topt_add_return_path | topt_no_body; - uschar boundaryStr[64]; + uschar * bound; - DEBUG(D_deliver) debug_printf("sending error message to: %s\n", sender_address); + DEBUG(D_deliver) + debug_printf("sending error message to: %s\n", sender_address); /* build unique id for MIME boundary */ - snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d", - time(NULL), rand()); - DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", boundaryStr); + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", bound); if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); @@ -6512,11 +6762,10 @@ "This message was created automatically by mail delivery software.\n" " ----- The following addresses had successful delivery notifications -----\n", - qualify_domain_sender, sender_address, boundaryStr, boundaryStr); + qualify_domain_sender, sender_address, bound, bound); - addr_dsntmp = addr_senddsn; - while(addr_dsntmp) - { + for (addr_dsntmp = addr_senddsn; addr_dsntmp; + addr_dsntmp = addr_dsntmp->next) fprintf(f, "<%s> (relayed %s)\n\n", addr_dsntmp->address, (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 @@ -6525,15 +6774,14 @@ ? "to non-DSN-aware mailer" : "via non \"Remote SMTP\" router" ); - addr_dsntmp = addr_dsntmp->next; - } + fprintf(f, "--%s\n" "Content-type: message/delivery-status\n\n" "Reporting-MTA: dns; %s\n", - boundaryStr, smtp_active_hostname); + bound, smtp_active_hostname); - if (dsn_envid != NULL) { - /* must be decoded from xtext: see RFC 3461:6.3a */ + if (dsn_envid) + { /* must be decoded from xtext: see RFC 3461:6.3a */ uschar *xdec_envid; if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); @@ -6555,15 +6803,14 @@ addr_dsntmp->address); if (addr_dsntmp->host_used && addr_dsntmp->host_used->name) - fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n", + fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n", addr_dsntmp->host_used->name); else - fprintf(f,"Diagnostic-Code: X-Exim; relayed via non %s router\n", + fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n", (addr_dsntmp->dsn_flags & rf_dsnlasthop) == 1 ? "DSN" : "SMTP"); - fputc('\n', f); } - fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", boundaryStr); + fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); fflush(f); transport_filter_argv = NULL; /* Just in case */ @@ -6573,15 +6820,13 @@ transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); fflush(f); - fprintf(f,"\n"); - fprintf(f,"--%s--\n", boundaryStr); + fprintf(f,"\n--%s--\n", bound); fflush(f); fclose(f); rc = child_close(pid, 0); /* Waits for child to close, no timeout */ } } -#endif /*EXPERIMENTAL_DSN*/ /* If any addresses failed, we must send a message to somebody, unless af_ignore_error is set, in which case no action is taken. It is possible for @@ -6625,10 +6870,10 @@ If neither of these cases obtains, something has gone wrong. Log the incident, but then ignore the error. */ - if (sender_address[0] == 0 && addr_failed->p.errors_address == NULL) + if (sender_address[0] == 0 && addr_failed->prop.errors_address == NULL) { - if (!testflag(addr_failed, af_retry_timedout) && - !testflag(addr_failed, af_ignore_error)) + if ( !testflag(addr_failed, af_retry_timedout) + && !testflag(addr_failed, af_ignore_error)) { log_write(0, LOG_MAIN|LOG_PANIC, "internal error: bounce message " "failure is neither frozen nor ignored (it's been ignored)"); @@ -6640,22 +6885,20 @@ it from the list, throw away any saved message file, log it, and mark the recipient done. */ - if (testflag(addr_failed, af_ignore_error) -#ifdef EXPERIMENTAL_DSN - || (((addr_failed->dsn_flags & rf_dsnflags) != 0) - && ((addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure)) -#endif + if ( testflag(addr_failed, af_ignore_error) + || ( addr_failed->dsn_flags & rf_dsnflags + && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure) ) - { + { addr = addr_failed; addr_failed = addr->next; if (addr->return_filename != NULL) Uunlink(addr->return_filename); log_write(0, LOG_MAIN, "%s%s%s%s: error ignored", addr->address, - (addr->parent == NULL)? US"" : US" <", - (addr->parent == NULL)? US"" : addr->parent->address, - (addr->parent == NULL)? US"" : US">"); + !addr->parent ? US"" : US" <", + !addr->parent ? US"" : addr->parent->address, + !addr->parent ? US"" : US">"); address_done(addr, logtod); child_done(addr, logtod); @@ -6671,16 +6914,12 @@ else { - bounce_recipient = (addr_failed->p.errors_address == NULL)? - sender_address : addr_failed->p.errors_address; + bounce_recipient = addr_failed->prop.errors_address + ? addr_failed->prop.errors_address : sender_address; /* Make a subprocess to send a message */ - pid = child_open_exim(&fd); - - /* Creation of child failed */ - - if (pid < 0) + if ((pid = child_open_exim(&fd)) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " "create child process to send failure message: %s", getpid(), getppid(), strerror(errno)); @@ -6698,12 +6937,10 @@ BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0; int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) * DELIVER_IN_BUFFER_SIZE; -#ifdef EXPERIMENTAL_DSN - uschar boundaryStr[64]; + uschar * bound; uschar *dsnlimitmsg; uschar *dsnnotifyhdr; int topt; -#endif DEBUG(D_deliver) debug_printf("sending error message to: %s\n", bounce_recipient); @@ -6713,20 +6950,16 @@ paddr = &addr_failed; for (addr = addr_failed; addr != NULL; addr = *paddr) - { - if (Ustrcmp(bounce_recipient, (addr->p.errors_address == NULL)? - sender_address : addr->p.errors_address) != 0) - { - paddr = &(addr->next); /* Not the same; skip */ - } - else /* The same - dechain */ - { + if (Ustrcmp(bounce_recipient, addr->prop.errors_address + ? addr->prop.errors_address : sender_address) == 0) + { /* The same - dechain */ *paddr = addr->next; *pmsgchain = addr; addr->next = NULL; pmsgchain = &(addr->next); } - } + else + paddr = &addr->next; /* Not the same; skip */ /* Include X-Failed-Recipients: for automatic interpretation, but do not let any one header line get too long. We do this by starting a @@ -6751,22 +6984,19 @@ /* Output the standard headers */ - if (errors_reply_to != NULL) + if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); fprintf(f, "Auto-Submitted: auto-replied\n"); moan_write_from(f); fprintf(f, "To: %s\n", bounce_recipient); -#ifdef EXPERIMENTAL_DSN /* generate boundary string and output MIME-Headers */ - snprintf(boundaryStr, sizeof(boundaryStr)-1, TIME_T_FMT "-eximdsn-%d", - time(NULL), rand()); + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); fprintf(f, "Content-Type: multipart/report;" " report-type=delivery-status; boundary=%s\n" "MIME-Version: 1.0\n", - boundaryStr); -#endif + bound); /* Open a template file if one is provided. Log failure to open, but carry on - default texts will be used. */ @@ -6791,12 +7021,10 @@ fprintf(f, "Subject: Mail delivery failed%s\n\n", to_sender? ": returning message to sender" : ""); -#ifdef EXPERIMENTAL_DSN /* output human readable part as text/plain section */ fprintf(f, "--%s\n" "Content-type: text/plain; charset=us-ascii\n\n", - boundaryStr); -#endif + bound); if ((emf_text = next_emf(emf, US"intro"))) fprintf(f, "%s", CS emf_text); @@ -6923,12 +7151,19 @@ fputc('\n', f); } -#ifdef EXPERIMENTAL_DSN /* output machine readable part */ - fprintf(f, "--%s\n" - "Content-type: message/delivery-status\n\n" - "Reporting-MTA: dns; %s\n", - boundaryStr, smtp_active_hostname); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (message_smtputf8) + fprintf(f, "--%s\n" + "Content-type: message/global-delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); + else +#endif + fprintf(f, "--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); if (dsn_envid) { @@ -6948,10 +7183,13 @@ "Status: 5.0.0\n", addr->address); if (addr->host_used && addr->host_used->name) - fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", - addr->host_used->name, addr->basic_errno); + { + fprintf(f, "Remote-MTA: dns; %s\n", + addr->host_used->name); + print_dsn_diagnostic_code(addr, f); + } + fputc('\n', f); } -#endif /* Now copy the message, trying to give an intelligible comment if it is too long for it all to be copied. The limit isn't strictly @@ -6960,63 +7198,6 @@ emf_text = next_emf(emf, US"copy"); -#ifndef EXPERIMENTAL_DSN - if (bounce_return_message) - { - int topt = topt_add_return_path; - if (!bounce_return_body) topt |= topt_no_body; - - if (emf_text) - fprintf(f, "%s", CS emf_text); - else - { - if (bounce_return_body) fprintf(f, -"------ This is a copy of the message, including all the headers. ------\n"); - else fprintf(f, -"------ This is a copy of the message's headers. ------\n"); - } - - /* While reading the "truncated" message, set return_size_limit to - the actual max testing value, rounded. We need to read the message - whether we are going to use it or not. */ - - { - int temp = bounce_return_size_limit; - bounce_return_size_limit = (max/1000)*1000; - emf_text = next_emf(emf, US"truncated"); - bounce_return_size_limit = temp; - } - - if (bounce_return_body && bounce_return_size_limit > 0) - { - struct stat statbuf; - if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max) - if (emf_text) - fprintf(f, "%s", CS emf_text); - else - fprintf(f, -"------ The body of the message is " OFF_T_FMT " characters long; only the first\n" -"------ %d or so are included here.\n", statbuf.st_size, max); - } - - fputc('\n', f); - fflush(f); - - transport_filter_argv = NULL; /* Just in case */ - return_path = sender_address; /* In case not previously set */ - transport_write_message(NULL, fileno(f), topt, - bounce_return_size_limit, NULL, NULL, NULL, NULL, NULL, 0); - } - - /* Write final text and close the template file if one is open */ - - if (emf) - { - if ((emf_text = next_emf(emf, US"final"))) - fprintf(f, "%s", CS emf_text); - (void)fclose(emf); - } -#else /* add message body we ignore the intro text from template and add the text for bounce_return_size_limit at the end. @@ -7028,7 +7209,7 @@ bounce_return_size_limit is always honored. */ - fprintf(f, "\n--%s\n", boundaryStr); + fprintf(f, "--%s\n", bound); dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; dsnnotifyhdr = NULL; @@ -7057,10 +7238,16 @@ } } - if (topt & topt_no_body) - fprintf(f,"Content-type: text/rfc822-headers\n\n"); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (message_smtputf8) + fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" + : "Content-type: message/global\n\n", + f); else - fprintf(f,"Content-type: message/rfc822\n\n"); +#endif + fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n" + : "Content-type: message/rfc822\n\n", + f); fflush(f); transport_filter_argv = NULL; /* Just in case */ @@ -7073,8 +7260,7 @@ if (emf) (void)fclose(emf); - fprintf(f, "\n--%s--\n", boundaryStr); -#endif /*EXPERIMENTAL_DSN*/ + fprintf(f, "\n--%s--\n", bound); /* Close the file, which should send an EOF to the child process that is receiving the message. Wait for it to finish. */ @@ -7161,11 +7347,9 @@ "msglog.OLD directory", spoolname); } else - { if (Uunlink(spoolname) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s: %s", spoolname, strerror(errno)); - } } /* Remove the two message files. */ @@ -7189,7 +7373,11 @@ /* Unset deliver_freeze so that we won't try to move the spool files further down */ deliver_freeze = FALSE; - } + +#ifdef EXPERIMENTAL_EVENT + (void) event_raise(event_action, US"msg:complete", NULL); +#endif +} /* If there are deferred addresses, we are keeping this message because it is not yet completed. Lose any temporary files that were catching output from @@ -7235,7 +7423,8 @@ if (deliver_domain != NULL) { - uschar *d = (testflag(addr, af_pfr))? addr->parent->domain : addr->domain; + const uschar *d = testflag(addr, af_pfr) + ? addr->parent->domain : addr->domain; /* The domain may be unset for an address that has never been routed because the system filter froze the message. */ @@ -7273,7 +7462,7 @@ DEBUG(D_deliver) debug_printf("one_time: adding %s in place of %s\n", otaddr->address, otaddr->parent->address); receive_add_recipient(otaddr->address, t); - recipients_list[recipients_count-1].errors_to = otaddr->p.errors_address; + recipients_list[recipients_count-1].errors_to = otaddr->prop.errors_address; tree_add_nonrecipient(otaddr->parent->address); update_spool = TRUE; } @@ -7285,7 +7474,7 @@ if (sender_address[0] != 0) { - if (addr->p.errors_address == NULL) + if (addr->prop.errors_address == NULL) { if (Ustrstr(recipients, sender_address) == NULL) recipients = string_sprintf("%s%s%s", recipients, @@ -7293,9 +7482,9 @@ } else { - if (Ustrstr(recipients, addr->p.errors_address) == NULL) + if (Ustrstr(recipients, addr->prop.errors_address) == NULL) recipients = string_sprintf("%s%s%s", recipients, - (recipients[0] == 0)? "" : ",", addr->p.errors_address); + (recipients[0] == 0)? "" : ",", addr->prop.errors_address); } } } @@ -7305,15 +7494,18 @@ is not sent. Another attempt will be made at the next delivery attempt (if it also defers). */ - if (!queue_2stage && delivery_attempted && -#ifdef EXPERIMENTAL_DSN - (((addr_defer->dsn_flags & rf_dsnflags) == 0) || - (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay) && -#endif - delay_warning[1] > 0 && sender_address[0] != 0 && - (delay_warning_condition == NULL || - expand_check_condition(delay_warning_condition, - US"delay_warning", US"option"))) + if ( !queue_2stage + && delivery_attempted + && ( ((addr_defer->dsn_flags & rf_dsnflags) == 0) + || (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay + ) + && delay_warning[1] > 0 + && sender_address[0] != 0 + && ( delay_warning_condition == NULL + || expand_check_condition(delay_warning_condition, + US"delay_warning", US"option") + ) + ) { int count; int show_time; @@ -7374,9 +7566,7 @@ uschar *wmf_text; FILE *wmf = NULL; FILE *f = fdopen(fd, "wb"); -#ifdef EXPERIMENTAL_DSN - uschar boundaryStr[64]; -#endif + uschar * bound; if (warn_message_file) { @@ -7397,16 +7587,13 @@ moan_write_from(f); fprintf(f, "To: %s\n", recipients); -#ifdef EXPERIMENTAL_DSN /* generated boundary string and output MIME-Headers */ - snprintf(boundaryStr, sizeof(boundaryStr)-1, - TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); fprintf(f, "Content-Type: multipart/report;" " report-type=delivery-status; boundary=%s\n" "MIME-Version: 1.0\n", - boundaryStr); -#endif + bound); if ((wmf_text = next_emf(wmf, US"header"))) fprintf(f, "%s\n", wmf_text); @@ -7414,12 +7601,10 @@ fprintf(f, "Subject: Warning: message %s delayed %s\n\n", message_id, warnmsg_delay); -#ifdef EXPERIMENTAL_DSN /* output human readable part as text/plain section */ fprintf(f, "--%s\n" "Content-type: text/plain; charset=us-ascii\n\n", - boundaryStr); -#endif + bound); if ((wmf_text = next_emf(wmf, US"intro"))) fprintf(f, "%s", CS wmf_text); @@ -7458,10 +7643,8 @@ /* List the addresses, with error information if allowed */ -#ifdef EXPERIMENTAL_DSN /* store addr_defer for machine readable part */ address_item *addr_dsndefer = addr_defer; -#endif fputc('\n', f); while (addr_defer) { @@ -7490,12 +7673,11 @@ "and when that happens, the message will be returned to you.\n"); } -#ifdef EXPERIMENTAL_DSN /* output machine readable part */ fprintf(f, "\n--%s\n" "Content-type: message/delivery-status\n\n" "Reporting-MTA: dns; %s\n", - boundaryStr, + bound, smtp_active_hostname); @@ -7510,23 +7692,27 @@ } fputc('\n', f); - while (addr_dsndefer) + for ( ; addr_dsndefer; addr_dsndefer = addr_dsndefer->next) { if (addr_dsndefer->dsn_orcpt) - fprintf(f,"Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); + fprintf(f, "Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); - fprintf(f,"Action: delayed\n"); - fprintf(f,"Final-Recipient: rfc822;%s\n", addr_dsndefer->address); - fprintf(f,"Status: 4.0.0\n"); + fprintf(f, "Action: delayed\n" + "Final-Recipient: rfc822;%s\n" + "Status: 4.0.0\n", + addr_dsndefer->address); if (addr_dsndefer->host_used && addr_dsndefer->host_used->name) - fprintf(f,"Remote-MTA: dns; %s\nDiagnostic-Code: smtp; %d\n", - addr_dsndefer->host_used->name, addr_dsndefer->basic_errno); - addr_dsndefer = addr_dsndefer->next; + { + fprintf(f, "Remote-MTA: dns; %s\n", + addr_dsndefer->host_used->name); + print_dsn_diagnostic_code(addr_dsndefer, f); + } + fputc('\n', f); } - fprintf(f, "\n--%s\n" + fprintf(f, "--%s\n" "Content-type: text/rfc822-headers\n\n", - boundaryStr); + bound); fflush(f); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ @@ -7537,10 +7723,9 @@ transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0); fflush(f); - fprintf(f,"\n--%s--\n", boundaryStr); + fprintf(f,"\n--%s--\n", bound); fflush(f); -#endif /*EXPERIMENTAL_DSN*/ /* Close and wait for child process to complete, without a timeout. If there's an error, don't update the count. */ @@ -7653,10 +7838,10 @@ /* Move the message off the spool if reqested */ - #ifdef SUPPORT_MOVE_FROZEN_MESSAGES +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES if (deliver_freeze && move_frozen_messages) (void)spool_move_message(id, message_subdir, US"", US"F"); - #endif +#endif } /* Closing the data file frees the lock; if the file has been unlinked it @@ -7678,6 +7863,60 @@ return final_yield; } + + +void +deliver_init(void) +{ +if (!regex_PIPELINING) regex_PIPELINING = + regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE); + +if (!regex_SIZE) regex_SIZE = + regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE); + +if (!regex_AUTH) regex_AUTH = + regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", + FALSE, TRUE); + +#ifdef SUPPORT_TLS +if (!regex_STARTTLS) regex_STARTTLS = + regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); +#endif + +#ifndef DISABLE_PRDR +if (!regex_PRDR) regex_PRDR = + regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); +#endif + +#ifdef EXPERIMENTAL_INTERNATIONAL +if (!regex_UTF8) regex_UTF8 = + regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE); +#endif + +if (!regex_DSN) regex_DSN = + regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); + +if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA = + regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE); +} + + +uschar * +deliver_get_sender_address (uschar * id) +{ +if (!spool_open_datafile(id)) + return NULL; + +sprintf(CS spoolname, "%s-H", id); +if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK) + return NULL; + +(void)close(deliver_datafile); +deliver_datafile = -1; + +return sender_address; +} + /* vi: aw ai sw=2 */ /* End of deliver.c */ diff -Nru exim4-4.84/src/dkim.c exim4-4.86~RC4/src/dkim.c --- exim4-4.84/src/dkim.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/dkim.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge, 1995 - 2007 */ +/* Copyright (c) University of Cambridge, 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for DKIM support. Other DKIM relevant code is in @@ -383,12 +383,11 @@ } -uschar *dkim_exim_sign(int dkim_fd, - uschar *dkim_private_key, - uschar *dkim_domain, - uschar *dkim_selector, - uschar *dkim_canon, - uschar *dkim_sign_headers) { +uschar * +dkim_exim_sign(int dkim_fd, uschar *dkim_private_key, + const uschar *dkim_domain, uschar *dkim_selector, + uschar *dkim_canon, uschar *dkim_sign_headers) +{ int sep = 0; uschar *seen_items = NULL; int seen_items_size = 0; @@ -412,7 +411,7 @@ store_pool = POOL_MAIN; - dkim_domain = expand_string(dkim_domain); + dkim_domain = expand_cstring(dkim_domain); if (dkim_domain == NULL) { /* expansion error, do not send message. */ log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " @@ -429,7 +428,7 @@ /* Only sign once for each domain, no matter how often it appears in the expanded list. */ if (seen_items != NULL) { - uschar *seen_items_list = seen_items; + const uschar *seen_items_list = seen_items; if (match_isinlist(dkim_signing_domain, &seen_items_list,0,NULL,NULL,MCL_STRING,TRUE,NULL) == OK) continue; diff -Nru exim4-4.84/src/dkim.h exim4-4.86~RC4/src/dkim.h --- exim4-4.84/src/dkim.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/dkim.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,10 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge, 1995 - 2007 */ +/* Copyright (c) University of Cambridge, 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -uschar *dkim_exim_sign(int,uschar *,uschar *,uschar *,uschar *,uschar *); +uschar *dkim_exim_sign(int, uschar *, const uschar *, uschar *, uschar *, uschar *); void dkim_exim_verify_init(void); void dkim_exim_verify_feed(uschar *, int); void dkim_exim_verify_finish(void); diff -Nru exim4-4.84/src/dns.c exim4-4.86~RC4/src/dns.c --- exim4-4.84/src/dns.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/dns.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for interfacing with the DNS. */ @@ -10,16 +10,6 @@ #include "exim.h" -/* Function declaration needed for mutual recursion when A6 records -are supported. */ - -#if HAVE_IPV6 -#ifdef SUPPORT_A6 -static void dns_complete_a6(dns_address ***, dns_answer *, dns_record *, - int, uschar *); -#endif -#endif - /************************************************* * Fake DNS resolver * @@ -33,7 +23,7 @@ also return a code specifying that the name should be passed on. Background: the original test suite required a real nameserver to carry the -test zones, whereas the new test suit has the fake server for portability. This +test zones, whereas the new test suite has the fake server for portability. This code supports both. Arguments: @@ -46,7 +36,7 @@ */ static int -fakens_search(uschar *domain, int type, uschar *answerptr, int size) +fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { int len = Ustrlen(domain); int asize = size; /* Locally modified */ @@ -63,36 +53,10 @@ name[len] = 0; endname = name + len; -/* This code, for forcing TRY_AGAIN and NO_RECOVERY, is here so that it works -for the old test suite that uses a real nameserver. When the old test suite is -eventually abandoned, this code could be moved into the fakens utility. */ - -if (len >= 14 && Ustrcmp(endname - 14, "test.again.dns") == 0) - { - int delay = Uatoi(name); /* digits at the start of the name */ - DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n", - name, dns_text_type(type)); - if (delay > 0) - { - DEBUG(D_dns) debug_printf("delaying %d seconds\n", delay); - sleep(delay); - } - h_errno = TRY_AGAIN; - return -1; - } - -if (len >= 13 && Ustrcmp(endname - 13, "test.fail.dns") == 0) - { - DEBUG(D_dns) debug_printf("Return from DNS lookup of %s (%s) faked for testing\n", - name, dns_text_type(type)); - h_errno = NO_RECOVERY; - return -1; - } - /* Look for the fakens utility, and if it exists, call it. */ -(void)string_format(utilname, sizeof(utilname), "%s/../bin/fakens", - spool_directory); +(void)string_format(utilname, sizeof(utilname), "%s/bin/fakens", + config_main_directory); if (stat(CS utilname, &statbuf) >= 0) { @@ -100,11 +64,10 @@ int infd, outfd, rc; uschar *argv[5]; - DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", - name, dns_text_type(type)); + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) using fakens\n", name, dns_text_type(type)); argv[0] = utilname; - argv[1] = spool_directory; + argv[1] = config_main_directory; argv[2] = name; argv[3] = dns_text_type(type); argv[4] = NULL; @@ -123,6 +86,13 @@ asize -= rc; /* may need to be passed on to res_search(). */ } + /* If we ran out of output buffer before exhasting the return, + carry on reading and counting it. */ + + if (asize == 0) + while ((rc = read(outfd, name, sizeof(name))) > 0) + len += rc; + if (rc < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "read from fakens failed: %s", strerror(errno)); @@ -139,6 +109,10 @@ DEBUG(D_dns) debug_printf("fakens returned PASS_ON\n"); } } +else + { + DEBUG(D_dns) debug_printf("fakens (%s) not found\n", utilname); + } /* fakens utility not found, or it returned "pass on" */ @@ -257,9 +231,9 @@ */ void -dns_build_reverse(uschar *string, uschar *buffer) +dns_build_reverse(const uschar *string, uschar *buffer) { -uschar *p = string + Ustrlen(string); +const uschar *p = string + Ustrlen(string); uschar *pp = buffer; /* Handle IPv4 address */ @@ -271,7 +245,7 @@ int i; for (i = 0; i < 4; i++) { - uschar *ppp = p; + const uschar *ppp = p; while (ppp > string && ppp[-1] != '.') ppp--; Ustrncpy(pp, ppp, p - ppp); pp += p - ppp; @@ -428,6 +402,33 @@ } +/* Extract the AUTHORITY information from the answer. If the +answer isn't authoritive (AA not set), we do not extract anything. + +The AUTHORITIVE section contains NS records if +the name in question was found, it contains a SOA record +otherwise. (This is just from experience and some tests, is there +some spec?) + +We've cycle through the AUTHORITY section, since it may contain +other records (e.g. NSEC3) too. */ + +static const uschar * +dns_extract_auth_name(const dns_answer * dnsa) /* FIXME: const dns_answer */ +{ +dns_scan dnss; +dns_record * rr; +HEADER * h = (HEADER *) dnsa->answer; + +if (!h->nscount || !h->aa) return NULL; +for (rr = dns_next_rr((dns_answer*) dnsa, &dnss, RESET_AUTHORITY); + rr; + rr = dns_next_rr((dns_answer*) dnsa, &dnss, RESET_NEXT)) + if (rr->type == (h->ancount ? T_NS : T_SOA)) return rr->name; +return NULL; +} + + /************************************************* @@ -436,25 +437,68 @@ /* We do not perform DNSSEC work ourselves; if the administrator has installed a verifying resolver which sets AD as appropriate, though, we'll use that. -(AD = Authentic Data) +(AD = Authentic Data, AA = Authoritive Answer) Argument: pointer to dns answer block Returns: bool indicating presence of AD bit */ BOOL -dns_is_secure(dns_answer *dnsa) +dns_is_secure(const dns_answer * dnsa) { #ifdef DISABLE_DNSSEC DEBUG(D_dns) debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n"); return FALSE; #else -HEADER *h = (HEADER *)dnsa->answer; -return h->ad ? TRUE : FALSE; +HEADER * h = (HEADER *) dnsa->answer; +const uschar * auth_name; +const uschar * trusted; + +if (h->ad) return TRUE; + +/* If the resolver we ask is authoritive for the domain in question, it +* may not set the AD but the AA bit. If we explicitly trust +* the resolver for that domain (via a domainlist in dns_trust_aa), +* we return TRUE to indicate a secure answer. +*/ + +if ( !h->aa + || !dns_trust_aa + || !(trusted = expand_string(dns_trust_aa)) + || !*trusted + || !(auth_name = dns_extract_auth_name(dnsa)) + || OK != match_isinlist(auth_name, &trusted, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) + ) + return FALSE; + +DEBUG(D_dns) debug_printf("DNS faked the AD bit " + "(got AA and matched with dns_trust_aa (%s in %s))\n", + auth_name, dns_trust_aa); + +return TRUE; #endif } +static void +dns_set_insecure(dns_answer * dnsa) +{ +HEADER * h = (HEADER *)dnsa->answer; +h->ad = 0; +} + +/************************************************ + * Check whether the AA bit is set * + * We need this to warn if we requested AD * + * from an authoritive server * + ************************************************/ + +BOOL +dns_is_aa(const dns_answer *dnsa) +{ +return ((HEADER*)dnsa->answer)->aa; +} @@ -510,7 +554,7 @@ */ static int -dns_return(uschar *name, int type, int rc) +dns_return(const uschar * name, int type, int rc) { res_state resp = os_get_dns_resolver_res(); tree_node *node = store_get_perm(sizeof(tree_node) + 290); @@ -521,8 +565,6 @@ return rc; } - - /************************************************* * Do basic DNS lookup * *************************************************/ @@ -549,11 +591,11 @@ */ int -dns_basic_lookup(dns_answer *dnsa, uschar *name, int type) +dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type) { #ifndef STAND_ALONE int rc = -1; -uschar *save; +const uschar *save_domain; #endif res_state resp = os_get_dns_resolver_res(); @@ -579,6 +621,25 @@ return previous->data.val; } +#ifdef EXPERIMENTAL_INTERNATIONAL +/* Convert all names to a-label form before doing lookup */ + { + uschar * alabel; + uschar * errstr = NULL; + DEBUG(D_dns) if (string_is_utf8(name)) + debug_printf("convert utf8 '%s' to alabel for for lookup\n", name); + if ((alabel = string_domain_utf8_to_alabel(name, &errstr)), errstr) + { + DEBUG(D_dns) + debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s\n", name, + errstr); + host_find_failed_syntax = TRUE; + return DNS_NOMATCH; + } + name = alabel; + } +#endif + /* If configured, check the hygene of the name passed to lookup. Otherwise, although DNS lookups may give REFUSED at the lower level, some resolvers turn this into TRY_AGAIN, which is silly. Give a NOMATCH return, since such @@ -597,23 +658,21 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT) { - uschar *checkname = name; + const uschar *checkname = name; int ovector[3*(EXPAND_MAXN+1)]; - if (regex_check_dns_names == NULL) - regex_check_dns_names = - regex_must_compile(check_dns_names_pattern, FALSE, TRUE); + dns_pattern_init(); /* For an SRV lookup, skip over the first two components (the service and protocol names, which both start with an underscore). */ - if (type == T_SRV) + if (type == T_SRV || type == T_TLSA) { while (*checkname++ != '.'); while (*checkname++ != '.'); } - if (pcre_exec(regex_check_dns_names, NULL, CS checkname, Ustrlen(checkname), + if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname), 0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0) { DEBUG(D_dns) @@ -635,22 +694,16 @@ nameservers are also believed to do this. It is, of course, contrary to the specification of the DNS, so we lock it out. */ -if (( - #ifdef SUPPORT_A6 - type == T_A6 || - #endif - type == T_A || type == T_AAAA) && - string_is_ip_address(name, NULL) != 0) +if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0) return DNS_NOMATCH; /* If we are running in the test harness, instead of calling the normal resolver (res_search), we call fakens_search(), which recognizes certain special domains, and interfaces to a fake nameserver for certain special zones. */ -if (running_in_test_harness) - dnsa->answerlen = fakens_search(name, type, dnsa->answer, MAXPACKET); -else - dnsa->answerlen = res_search(CS name, C_IN, type, dnsa->answer, MAXPACKET); +dnsa->answerlen = running_in_test_harness + ? fakens_search(name, type, dnsa->answer, MAXPACKET) + : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET); if (dnsa->answerlen > MAXPACKET) { @@ -671,12 +724,12 @@ name, dns_text_type(type)); /* Cut this out for various test programs */ - #ifndef STAND_ALONE - save = deliver_domain; - deliver_domain = name; /* set $domain */ - rc = match_isinlist(name, &dns_again_means_nonexist, 0, NULL, NULL, +#ifndef STAND_ALONE + save_domain = deliver_domain; + deliver_domain = string_copy(name); /* set $domain */ + rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL); - deliver_domain = save; + deliver_domain = save_domain; if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); @@ -686,9 +739,9 @@ "DNS_NOMATCH\n", name); return dns_return(name, type, DNS_NOMATCH); - #else /* For stand-alone tests */ +#else /* For stand-alone tests */ return dns_return(name, type, DNS_AGAIN); - #endif +#endif case NO_RECOVERY: DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave NO_RECOVERY\n" @@ -732,7 +785,8 @@ If fully_qualified_name is not NULL, set it to point to the full name returned by the resolver, if this is different to what it is given, unless the returned name starts with "*" as some nameservers seem to be returning -wildcards in this form. +wildcards in this form. In international mode "different" means "alabel +forms are different". Arguments: dnsa pointer to dns_answer structure @@ -749,10 +803,12 @@ */ int -dns_lookup(dns_answer *dnsa, uschar *name, int type, uschar **fully_qualified_name) +dns_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { int i; -uschar *orig_name = name; +const uschar *orig_name = name; +BOOL secure_so_far = TRUE; /* Loop to follow CNAME chains so far, but no further... */ @@ -775,7 +831,7 @@ cname_rr.data = type_rr.data = NULL; for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) { if (rr->type == type) @@ -791,23 +847,29 @@ if (i == 0 && fully_qualified_name != NULL) { - if (cname_rr.data != NULL) - { - if (Ustrcmp(cname_rr.name, *fully_qualified_name) != 0 && - cname_rr.name[0] != '*') - *fully_qualified_name = string_copy_dnsdomain(cname_rr.name); - } - else if (type_rr.data != NULL) - { - if (Ustrcmp(type_rr.name, *fully_qualified_name) != 0 && - type_rr.name[0] != '*') - *fully_qualified_name = string_copy_dnsdomain(type_rr.name); - } + uschar * rr_name = cname_rr.data ? cname_rr.name + : type_rr.data ? type_rr.name : NULL; + if ( rr_name + && Ustrcmp(rr_name, *fully_qualified_name) != 0 + && rr_name[0] != '*' +#ifdef EXPERIMENTAL_INTERNATIONAL + && ( !string_is_utf8(*fully_qualified_name) + || Ustrcmp(rr_name, + string_domain_utf8_to_alabel(*fully_qualified_name, NULL)) != 0 + ) +#endif + ) + *fully_qualified_name = string_copy_dnsdomain(rr_name); } /* If any data records of the correct type were found, we are done. */ - if (type_rr.data != NULL) return DNS_SUCCEED; + if (type_rr.data != NULL) + { + if (!secure_so_far) /* mark insecure if any element of CNAME chain was */ + dns_set_insecure(dnsa); + return DNS_SUCCEED; + } /* If there are no data records, we need to re-scan the DNS using the domain given in the CNAME record, which should exist (otherwise we should @@ -816,10 +878,13 @@ if (cname_rr.data == NULL) return DNS_FAIL; datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256); + cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, sizeof(data)); if (datalen < 0) return DNS_FAIL; name = data; + if (!dns_is_secure(dnsa)) + secure_so_far = FALSE; + DEBUG(D_dns) debug_printf("CNAME found: change to %s\n", name); } /* Loop back to do another lookup */ @@ -839,7 +904,7 @@ * Do a DNS lookup and handle virtual types * ************************************************/ -/* This function handles some invented "lookup types" that synthesize feature +/* This function handles some invented "lookup types" that synthesize features not available in the basic types. The special types all have negative values. Positive type values are passed straight on to dns_lookup(). @@ -858,176 +923,181 @@ */ int -dns_special_lookup(dns_answer *dnsa, uschar *name, int type, - uschar **fully_qualified_name) +dns_special_lookup(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) { -if (type >= 0) return dns_lookup(dnsa, name, type, fully_qualified_name); - -/* The "mx hosts only" type doesn't require any special action here */ - -if (type == T_MXH) return dns_lookup(dnsa, name, T_MX, fully_qualified_name); - -/* Find nameservers for the domain or the nearest enclosing zone, excluding the -root servers. */ - -if (type == T_ZNS) +switch (type) { - uschar *d = name; - while (d != 0) + /* The "mx hosts only" type doesn't require any special action here */ + case T_MXH: + return dns_lookup(dnsa, name, T_MX, fully_qualified_name); + + /* Find nameservers for the domain or the nearest enclosing zone, excluding + the root servers. */ + case T_ZNS: + type = T_NS; + /* FALLTHROUGH */ + case T_SOA: { - int rc = dns_lookup(dnsa, d, T_NS, fully_qualified_name); - if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; - while (*d != 0 && *d != '.') d++; - if (*d++ == 0) break; + const uschar *d = name; + while (d != 0) + { + int rc = dns_lookup(dnsa, d, type, fully_qualified_name); + if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; + while (*d != 0 && *d != '.') d++; + if (*d++ == 0) break; + } + return DNS_NOMATCH; } - return DNS_NOMATCH; - } - -/* Try to look up the Client SMTP Authorization SRV record for the name. If -there isn't one, search from the top downwards for a CSA record in a parent -domain, which might be making assertions about subdomains. If we find a record -we set fully_qualified_name to whichever lookup succeeded, so that the caller -can tell whether to look at the explicit authorization field or the subdomain -assertion field. */ - -if (type == T_CSA) - { - uschar *srvname, *namesuff, *tld, *p; - int priority, weight, port; - int limit, rc, i; - BOOL ipv6; - dns_record *rr; - dns_scan dnss; - DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); - - srvname = string_sprintf("_client._smtp.%s", name); - rc = dns_lookup(dnsa, srvname, T_SRV, NULL); - if (rc == DNS_SUCCEED || rc == DNS_AGAIN) + /* Try to look up the Client SMTP Authorization SRV record for the name. If + there isn't one, search from the top downwards for a CSA record in a parent + domain, which might be making assertions about subdomains. If we find a record + we set fully_qualified_name to whichever lookup succeeded, so that the caller + can tell whether to look at the explicit authorization field or the subdomain + assertion field. */ + case T_CSA: { - if (rc == DNS_SUCCEED) *fully_qualified_name = name; - return rc; - } - - /* Search for CSA subdomain assertion SRV records from the top downwards, - starting with the 2nd level domain. This order maximizes cache-friendliness. - We skip the top level domains to avoid loading their nameservers and because - we know they'll never have CSA SRV records. */ - - namesuff = Ustrrchr(name, '.'); - if (namesuff == NULL) return DNS_NOMATCH; - tld = namesuff + 1; - ipv6 = FALSE; - limit = dns_csa_search_limit; + uschar *srvname, *namesuff, *tld, *p; + int priority, weight, port; + int limit, rc, i; + BOOL ipv6; + dns_record *rr; + dns_scan dnss; - /* Use more appropriate search parameters if we are in the reverse DNS. */ + DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); - if (strcmpic(namesuff, US".arpa") == 0) - { - if (namesuff - 8 > name && strcmpic(namesuff - 8, US".in-addr.arpa") == 0) - { - namesuff -= 8; - tld = namesuff + 1; - limit = 3; - } - else if (namesuff - 4 > name && strcmpic(namesuff - 4, US".ip6.arpa") == 0) + srvname = string_sprintf("_client._smtp.%s", name); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_SUCCEED || rc == DNS_AGAIN) { - namesuff -= 4; - tld = namesuff + 1; - ipv6 = TRUE; - limit = 3; + if (rc == DNS_SUCCEED) *fully_qualified_name = string_copy(name); + return rc; } - } - DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); + /* Search for CSA subdomain assertion SRV records from the top downwards, + starting with the 2nd level domain. This order maximizes cache-friendliness. + We skip the top level domains to avoid loading their nameservers and because + we know they'll never have CSA SRV records. */ + + namesuff = Ustrrchr(name, '.'); + if (namesuff == NULL) return DNS_NOMATCH; + tld = namesuff + 1; + ipv6 = FALSE; + limit = dns_csa_search_limit; + + /* Use more appropriate search parameters if we are in the reverse DNS. */ + + if (strcmpic(namesuff, US".arpa") == 0) + if (namesuff - 8 > name && strcmpic(namesuff - 8, US".in-addr.arpa") == 0) + { + namesuff -= 8; + tld = namesuff + 1; + limit = 3; + } + else if (namesuff - 4 > name && strcmpic(namesuff - 4, US".ip6.arpa") == 0) + { + namesuff -= 4; + tld = namesuff + 1; + ipv6 = TRUE; + limit = 3; + } + + DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); + + /* Do not perform the search if the top level or 2nd level domains do not + exist. This is quite common, and when it occurs all the search queries would + go to the root or TLD name servers, which is not friendly. So we check the + AUTHORITY section; if it contains the root's SOA record or the TLD's SOA then + the TLD or the 2LD (respectively) doesn't exist and we can skip the search. + If the TLD and the 2LD exist but the explicit CSA record lookup failed, then + the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */ - /* Do not perform the search if the top level or 2nd level domains do not - exist. This is quite common, and when it occurs all the search queries would - go to the root or TLD name servers, which is not friendly. So we check the - AUTHORITY section; if it contains the root's SOA record or the TLD's SOA then - the TLD or the 2LD (respectively) doesn't exist and we can skip the search. - If the TLD and the 2LD exist but the explicit CSA record lookup failed, then - the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */ - - if (rc == DNS_NOMATCH) - { - /* This is really gross. The successful return value from res_search() is - the packet length, which is stored in dnsa->answerlen. If we get a - negative DNS reply then res_search() returns -1, which causes the bounds - checks for name decompression to fail when it is treated as a packet - length, which in turn causes the authority search to fail. The correct - packet length has been lost inside libresolv, so we have to guess a - replacement value. (The only way to fix this properly would be to - re-implement res_search() and res_query() so that they don't muddle their - success and packet length return values.) For added safety we only reset - the packet length if the packet header looks plausible. */ - - HEADER *h = (HEADER *)dnsa->answer; - if (h->qr == 1 && h->opcode == QUERY && h->tc == 0 - && (h->rcode == NOERROR || h->rcode == NXDOMAIN) - && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 - && ntohs(h->nscount) >= 1) - dnsa->answerlen = MAXPACKET; - - for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - if (rr->type != T_SOA) continue; - else if (strcmpic(rr->name, US"") == 0 || - strcmpic(rr->name, tld) == 0) return DNS_NOMATCH; - else break; - } - - for (i = 0; i < limit; i++) - { - if (ipv6) + if (rc == DNS_NOMATCH) { - /* Scan through the IPv6 reverse DNS in chunks of 16 bits worth of IP - address, i.e. 4 hex chars and 4 dots, i.e. 8 chars. */ - namesuff -= 8; - if (namesuff <= name) return DNS_NOMATCH; + /* This is really gross. The successful return value from res_search() is + the packet length, which is stored in dnsa->answerlen. If we get a + negative DNS reply then res_search() returns -1, which causes the bounds + checks for name decompression to fail when it is treated as a packet + length, which in turn causes the authority search to fail. The correct + packet length has been lost inside libresolv, so we have to guess a + replacement value. (The only way to fix this properly would be to + re-implement res_search() and res_query() so that they don't muddle their + success and packet length return values.) For added safety we only reset + the packet length if the packet header looks plausible. */ + + HEADER *h = (HEADER *)dnsa->answer; + if (h->qr == 1 && h->opcode == QUERY && h->tc == 0 + && (h->rcode == NOERROR || h->rcode == NXDOMAIN) + && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 + && ntohs(h->nscount) >= 1) + dnsa->answerlen = MAXPACKET; + + for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) + ) + if (rr->type != T_SOA) continue; + else if (strcmpic(rr->name, US"") == 0 || + strcmpic(rr->name, tld) == 0) return DNS_NOMATCH; + else break; } - else - /* Find the start of the preceding domain name label. */ - do - if (--namesuff <= name) return DNS_NOMATCH; - while (*namesuff != '.'); - DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); - - srvname = string_sprintf("_client._smtp.%s", namesuff + 1); - rc = dns_lookup(dnsa, srvname, T_SRV, NULL); - if (rc == DNS_AGAIN) return rc; - if (rc != DNS_SUCCEED) continue; - - /* Check that the SRV record we have found is worth returning. We don't - just return the first one we find, because some lower level SRV record - might make stricter assertions than its parent domain. */ - - for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + for (i = 0; i < limit; i++) { - if (rr->type != T_SRV) continue; - - /* Extract the numerical SRV fields (p is incremented) */ - p = rr->data; - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); - - /* Check the CSA version number */ - if (priority != 1) continue; - - /* If it's making an interesting assertion, return this response. */ - if (port & 1) - { - *fully_qualified_name = namesuff + 1; - return DNS_SUCCEED; - } + if (ipv6) + { + /* Scan through the IPv6 reverse DNS in chunks of 16 bits worth of IP + address, i.e. 4 hex chars and 4 dots, i.e. 8 chars. */ + namesuff -= 8; + if (namesuff <= name) return DNS_NOMATCH; + } + else + /* Find the start of the preceding domain name label. */ + do + if (--namesuff <= name) return DNS_NOMATCH; + while (*namesuff != '.'); + + DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); + + srvname = string_sprintf("_client._smtp.%s", namesuff + 1); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_AGAIN) return rc; + if (rc != DNS_SUCCEED) continue; + + /* Check that the SRV record we have found is worth returning. We don't + just return the first one we find, because some lower level SRV record + might make stricter assertions than its parent domain. */ + + for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + { + if (rr->type != T_SRV) continue; + + /* Extract the numerical SRV fields (p is incremented) */ + p = rr->data; + GETSHORT(priority, p); + GETSHORT(weight, p); weight = weight; /* compiler quietening */ + GETSHORT(port, p); + + /* Check the CSA version number */ + if (priority != 1) continue; + + /* If it's making an interesting assertion, return this response. */ + if (port & 1) + { + *fully_qualified_name = namesuff + 1; + return DNS_SUCCEED; + } + } } + return DNS_NOMATCH; } - return DNS_NOMATCH; + + default: + if (type >= 0) + return dns_lookup(dnsa, name, type, fully_qualified_name); } /* Control should never reach here */ @@ -1037,164 +1107,6 @@ -/* Support for A6 records has been commented out since they were demoted to -experimental status at IETF 51. */ - -#if HAVE_IPV6 && defined(SUPPORT_A6) - -/************************************************* -* Search DNS block for prefix RRs * -*************************************************/ - -/* Called from dns_complete_a6() to search an additional section or a main -answer section for required prefix records to complete an IPv6 address obtained -from an A6 record. For each prefix record, a recursive call to dns_complete_a6 -is made, with a new copy of the address so far. - -Arguments: - dnsa the DNS answer block - which RESET_ADDITIONAL or RESET_ANSWERS - name name of prefix record - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - bits number of bits we have already got - bitvec the bits we have already got - -Returns: TRUE if any records were found -*/ - -static BOOL -dns_find_prefix(dns_answer *dnsa, int which, uschar *name, dns_address - ***yptrptr, int bits, uschar *bitvec) -{ -BOOL yield = FALSE; -dns_record *rr; -dns_scan dnss; - -for (rr = dns_next_rr(dnsa, &dnss, which); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - { - uschar cbitvec[16]; - if (rr->type != T_A6 || strcmpic(rr->name, name) != 0) continue; - yield = TRUE; - memcpy(cbitvec, bitvec, sizeof(cbitvec)); - dns_complete_a6(yptrptr, dnsa, rr, bits, cbitvec); - } - -return yield; -} - - - -/************************************************* -* Follow chains of A6 records * -*************************************************/ - -/* A6 records may be incomplete, with pointers to other records containing more -bits of the address. There can be a tree structure, leading to a number of -addresses originating from a single initial A6 record. - -Arguments: - yptrptr pointer to the pointer that points to where to hang the next - dns_address structure - dnsa the current DNS answer block - rr the RR we have at present - bits number of bits we have already got - bitvec the bits we have already got - -Returns: nothing -*/ - -static void -dns_complete_a6(dns_address ***yptrptr, dns_answer *dnsa, dns_record *rr, - int bits, uschar *bitvec) -{ -static uschar bitmask[] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 }; -uschar *p = (uschar *)(rr->data); -int prefix_len, suffix_len; -int i, j, k; -uschar *chainptr; -uschar chain[264]; -dns_answer cdnsa; - -/* The prefix length is the first byte. It defines the prefix which is missing -from the data in this record as a number of bits. Zero means this is the end of -a chain. The suffix is the data in this record; only sufficient bytes to hold -it are supplied. There may be zero bytes. We have to ignore trailing bits that -we have already obtained from earlier RRs in the chain. */ - -prefix_len = *p++; /* bits */ -suffix_len = (128 - prefix_len + 7)/8; /* bytes */ - -/* If the prefix in this record is greater than the prefix in the previous -record in the chain, we have to ignore the record (RFC 2874). */ - -if (prefix_len > 128 - bits) return; - -/* In this little loop, the number of bits up to and including the current byte -is held in k. If we have none of the bits in this byte, we can just or it into -the current data. If we have all of the bits in this byte, we skip it. -Otherwise, some masking has to be done. */ - -for (i = suffix_len - 1, j = 15, k = 8; i >= 0; i--) - { - int required = k - bits; - if (required >= 8) bitvec[j] |= p[i]; - else if (required > 0) bitvec[j] |= p[i] & bitmask[required]; - j--; /* I tried putting these in the "for" statement, but gcc muttered */ - k += 8; /* about computed values not being used. */ - } - -/* If the prefix_length is zero, we are at the end of a chain. Build a -dns_address item with the current data, hang it onto the end of the chain, -adjust the hanging pointer, and we are done. */ - -if (prefix_len == 0) - { - dns_address *new = store_get(sizeof(dns_address) + 50); - inet_ntop(AF_INET6, bitvec, CS new->address, 50); - new->next = NULL; - **yptrptr = new; - *yptrptr = &(new->next); - return; - } - -/* Prefix length is not zero. Reset the number of bits that we have collected -so far, and extract the chain name. */ - -bits = 128 - prefix_len; -p += suffix_len; - -chainptr = chain; -while ((i = *p++) != 0) - { - if (chainptr != chain) *chainptr++ = '.'; - memcpy(chainptr, p, i); - chainptr += i; - p += i; - } -*chainptr = 0; -chainptr = chain; - -/* Now scan the current DNS response record to see if the additional section -contains the records we want. This processing can be cut out for testing -purposes. */ - -if (dns_find_prefix(dnsa, RESET_ADDITIONAL, chainptr, yptrptr, bits, bitvec)) - return; - -/* No chain records were found in the current DNS response block. Do a new DNS -lookup to try to find these records. This opens up the possibility of DNS -failures. We ignore them at this point; if all branches of the tree fail, there -will be no addresses at the end. */ - -if (dns_lookup(&cdnsa, chainptr, T_A6, NULL) == DNS_SUCCEED) - (void)dns_find_prefix(&cdnsa, RESET_ANSWERS, chainptr, yptrptr, bits, bitvec); -} -#endif /* HAVE_IPV6 && defined(SUPPORT_A6) */ - - /************************************************* @@ -1202,59 +1114,58 @@ *************************************************/ /* The record type is either T_A for an IPv4 address or T_AAAA (or T_A6 when -supported) for an IPv6 address. In the A6 case, there may be several addresses, -generated by following chains. A recursive function does all the hard work. A6 -records now look like passing into history, so the code is only included when -explicitly asked for. +supported) for an IPv6 address. Argument: dnsa the DNS answer block rr the RR -Returns: pointer a chain of dns_address items +Returns: pointer to a chain of dns_address items; NULL when the dnsa was overrun */ dns_address * dns_address_from_rr(dns_answer *dnsa, dns_record *rr) { -dns_address *yield = NULL; - -#if HAVE_IPV6 && defined(SUPPORT_A6) -dns_address **yieldptr = &yield; -uschar bitvec[16]; -#else -dnsa = dnsa; /* Stop picky compilers warning */ -#endif +dns_address * yield = NULL; +uschar * dnsa_lim = dnsa->answer + dnsa->answerlen; if (rr->type == T_A) { - uschar *p = (uschar *)(rr->data); - yield = store_get(sizeof(dns_address) + 20); - (void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - yield->next = NULL; + uschar *p = US rr->data; + if (p + 4 <= dnsa_lim) + { + yield = store_get(sizeof(dns_address) + 20); + (void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + yield->next = NULL; + } } #if HAVE_IPV6 -#ifdef SUPPORT_A6 -else if (rr->type == T_A6) - { - memset(bitvec, 0, sizeof(bitvec)); - dns_complete_a6(&yieldptr, dnsa, rr, 0, bitvec); - } -#endif /* SUPPORT_A6 */ - else { - yield = store_get(sizeof(dns_address) + 50); - inet_ntop(AF_INET6, (uschar *)(rr->data), CS yield->address, 50); - yield->next = NULL; + if (rr->data + 16 <= dnsa_lim) + { + yield = store_get(sizeof(dns_address) + 50); + inet_ntop(AF_INET6, US rr->data, CS yield->address, 50); + yield->next = NULL; + } } #endif /* HAVE_IPV6 */ return yield; } + + +void +dns_pattern_init(void) +{ +if (check_dns_names_pattern[0] != 0 && !regex_check_dns_names) + regex_check_dns_names = + regex_must_compile(check_dns_names_pattern, FALSE, TRUE); +} + /* vi: aw ai sw=2 */ /* End of dns.c */ diff -Nru exim4-4.84/src/drtables.c exim4-4.86~RC4/src/drtables.c --- exim4-4.84/src/drtables.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/drtables.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -53,6 +53,10 @@ #include "auths/spa.h" #endif +#ifdef AUTH_TLS +#include "auths/tls.h" +#endif + auth_info auths_available[] = { /* Checking by an expansion condition on plain text */ @@ -154,6 +158,20 @@ NULL /* diagnostic function */ }, #endif + +#ifdef AUTH_TLS + { + US"tls", /* lookup name */ + auth_tls_options, + &auth_tls_options_count, + &auth_tls_option_defaults, + sizeof(auth_tls_options_block), + auth_tls_init, /* init function */ + auth_tls_server, /* server function */ + NULL, /* client function */ + NULL /* diagnostic function */ + }, +#endif { US"", NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL } }; diff -Nru exim4-4.84/src/EDITME exim4-4.86~RC4/src/EDITME --- exim4-4.84/src/EDITME 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/EDITME 2015-06-27 17:01:28.000000000 +0200 @@ -336,7 +336,7 @@ #------------------------------------------------------------------------------ -# The PCRE library is required for exim. There is no longer an embedded +# The PCRE library is required for Exim. There is no longer an embedded # version of the PCRE library included with the source code, instead you # must use a system library or build your own copy of PCRE. # In either case you must specify the library link info here. If the @@ -426,6 +426,7 @@ # By default, Exim has support for checking the AD bit in a DNS response, to # determine if DNSSEC validation was successful. If your system libraries # do not support that bit, then set DISABLE_DNSSEC to "yes" +# Note: Enabling EXPERIMENTAL_DANE unconditionally overrides this setting. # DISABLE_DNSSEC=yes @@ -473,9 +474,9 @@ # LDFLAGS += -lopendmarc -# Uncomment the following line to support Transport post-delivery actions, +# Uncomment the following line to support Events, # eg. for logging to a database. -# EXPERIMENTAL_TPDA=yes +# EXPERIMENTAL_EVENT=yes # Uncomment the following line to add Redis lookup support # You need to have hiredis installed on your system (https://github.com/redis/hiredis). @@ -487,12 +488,21 @@ # Uncomment the following line to enable Experimental Proxy Protocol # EXPERIMENTAL_PROXY=yes -# Uncomment the following line to enable support for checking certiticate +# Uncomment the following line to enable support for checking certificate # ownership # EXPERIMENTAL_CERTNAMES=yes -# Uncomment the following line to add DSN support -# EXPERIMENTAL_DSN=yes +# Uncomment the following line to add DANE support +# Note: Enabling this unconditionally overrides DISABLE_DNSSEC +# EXPERIMENTAL_DANE=yes + +# Uncomment the following line to add SOCKS support +# EXPERIMENTAL_SOCKS=yes + +# Uncomment the following to add Internationalisation features. You need to +# have the IDN library installed. +# EXPERIMENTAL_INTERNATIONAL=yes +# LDFLAGS += -lidn ############################################################################### # THESE ARE THINGS YOU MIGHT WANT TO SPECIFY # @@ -627,6 +637,7 @@ # AUTH_HEIMDAL_GSSAPI_PC=heimdal-gssapi # AUTH_PLAINTEXT=yes # AUTH_SPA=yes +# AUTH_TLS=yes #------------------------------------------------------------------------------ @@ -790,7 +801,7 @@ # with the extension "texinfo" in the doc directory. You may find that the # version number of the texinfo files is different to your Exim version number, # because the main documentation isn't updated as often as the code. For -# example, if you have Exim version 4.43, the source tarball upacks into a +# example, if you have Exim version 4.43, the source tarball unpacks into a # directory called exim-4.43, but the texinfo tarball unpacks into exim-4.40. # In this case, move the contents of exim-4.40/doc into exim-4.43/doc after you # have unpacked them. Then set INFO_DIRECTORY to the location of your info @@ -868,9 +879,15 @@ # If the exigrep utility is fed compressed log files, it tries to uncompress # them using this command. +# Leave it empty to enforce autodetection at runtime: +# ZCAT_COMMAND= +# +# Omit the path if you want to use your system's PATH: +# ZCAT_COMMAND=zcat +# +# Or specify the full pathname: ZCAT_COMMAND=/usr/bin/zcat - #------------------------------------------------------------------------------ # Compiling in support for embedded Perl: If you want to be able to # use Perl code in Exim's string manipulation language and you have Perl @@ -945,7 +962,7 @@ # There is no need to install all of SASL on your system. You just need to run # ./configure --with-pwcheck, cd to the pwcheck directory within the sources, # make and make install. You must create the socket directory (default -# /var/pwcheck) and chown it to exim's user and group. Once you have installed +# /var/pwcheck) and chown it to Exim's user and group. Once you have installed # pwcheck, you should arrange for it to be started by root at boot time. # CYRUS_PWCHECK_SOCKET=/var/pwcheck/pwcheck @@ -953,7 +970,7 @@ #------------------------------------------------------------------------------ # Support for authentication via the Cyrus SASL saslauthd daemon is available. -# The Exim support, which is intented for use in conjunction with the SMTP AUTH +# The Exim support, which is intended for use in conjunction with the SMTP AUTH # facilities, is included only when requested by setting the following # parameter to the location of the saslauthd daemon's socket. # @@ -961,7 +978,7 @@ # ./configure --with-saslauthd (and any other options you need, for example, to # select or deselect authentication mechanisms), cd to the saslauthd directory # within the sources, make and make install. You must create the socket -# directory (default /var/state/saslauthd) and chown it to exim's user and +# directory (default /var/state/saslauthd) and chown it to Exim's user and # group. Once you have installed saslauthd, you should arrange for it to be # started by root at boot time. @@ -1122,7 +1139,7 @@ # to handle the different cases. If CONFIGURE_FILE_USE_EUID is defined, then # Exim will first look for a configuration file whose name is that defined # by CONFIGURE_FILE, with the effective uid tacked on the end, separated by -# a period (for eximple, /usr/exim/configure.0). If this file does not exist, +# a period (for example, /usr/exim/configure.0). If this file does not exist, # then the bare configuration file name is tried. In the case when both # CONFIGURE_FILE_USE_EUID and CONFIGURE_FILE_USE_NODE are set, four files # are tried: .., ., ., and . @@ -1306,7 +1323,7 @@ #------------------------------------------------------------------------------ -# Expanding match_* second paramters: BE CAREFUL IF ENABLING THIS! +# Expanding match_* second parameters: BE CAREFUL IF ENABLING THIS! # It has proven too easy in practice for administrators to configure security # problems into their Exim install, by treating match_domain{}{} and friends # as a form of string comparison, where the second string comes from untrusted diff -Nru exim4-4.84/src/exigrep.src exim4-4.86~RC4/src/exigrep.src --- exim4-4.84/src/exigrep.src 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/exigrep.src 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ use strict; -# Copyright (c) 2007-2014 University of Cambridge. +# Copyright (c) 2007-2015 University of Cambridge. # See the file NOTICE for conditions of use and distribution. # Except when they appear in comments, the following placeholders in this @@ -60,6 +60,11 @@ my (%saved, %id_list, $pattern, $queue_time, $insensitive, $invert); +# If using "related" option, have to track extra message IDs +my $related; +my $related_re=''; +my @Mids = (); + sub do_line { # Convert syslog lines to mainlog format, as in eximstats. @@ -90,8 +95,16 @@ } else { - $id_list{$id} = 1 if defined $id_list{$id} || - ($insensitive && /$pattern/io) || /$pattern/o; + if (defined $id_list{$id} || + ($insensitive && /$pattern/io) || /$pattern/o) + { + $id_list{$id} = 1; + get_related_ids($id) if $related; + } + elsif ($related && $related_re) + { + grep_for_related($_, $id); + } } # See if this is a completion for some message. If it is interesting, @@ -173,16 +186,30 @@ return $cmdline; } +sub grep_for_related { + my ($line,$id) = @_; + $id_list{$id} = 1 if $line =~ m/$related_re/; +} + +sub get_related_ids { + my ($id) = @_; + push @Mids, $id unless grep /\b$id\b/, @Mids; + my $re = join '|', @Mids; + $related_re = qr/$re/; +} + # The main program. Extract the pattern and make sure any relevant characters # are quoted if the -l flag is given. The -t flag gives a time-on-queue value -# which is an additional condition. +# which is an additional condition. The -M flag will also display "related" +# loglines (msgid from matched lines is searched in following lines). -getopts('Ilvt:',\my %args); +getopts('Ilvt:M',\my %args); $queue_time = $args{'t'}? $args{'t'} : -1; $insensitive = $args{'I'}? 0 : 1; $invert = $args{'v'}? 1 : 0; +$related = $args{'M'}? 1 : 0; -die "usage: exigrep [-I] [-l] [-t ] [-v] []...\n" +die "usage: exigrep [-I] [-l] [-M] [-t ] [-v] []...\n" if ($#ARGV < 0); $pattern = shift @ARGV; @@ -197,7 +224,7 @@ foreach (@ARGV) { my $filename = $_; - if ($filename =~ /\.(?:COMPRESS_SUFFIX)$/o) + if (-x 'ZCAT_COMMAND' && $filename =~ /\.(?:COMPRESS_SUFFIX)$/o) { open(LOG, "ZCAT_COMMAND $filename |") || die "Unable to zcat $filename: $!\n"; diff -Nru exim4-4.84/src/exim.c exim4-4.86~RC4/src/exim.c --- exim4-4.84/src/exim.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/exim.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -12,6 +12,13 @@ #include "exim.h" +#ifdef USE_GNUTLS +# include +# if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP) +# define DISABLE_OCSP +# endif +#endif + extern void init_lookup_list(void); @@ -81,7 +88,7 @@ */ const pcre * -regex_must_compile(uschar *pattern, BOOL caseless, BOOL use_malloc) +regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc) { int offset; int options = PCRE_COPT; @@ -93,7 +100,7 @@ pcre_free = function_store_free; } if (caseless) options |= PCRE_CASELESS; -yield = pcre_compile(CS pattern, options, (const char **)&error, &offset, NULL); +yield = pcre_compile(CCS pattern, options, (const char **)&error, &offset, NULL); pcre_malloc = function_store_get; pcre_free = function_dummy_free; if (yield == NULL) @@ -124,10 +131,11 @@ */ BOOL -regex_match_and_setup(const pcre *re, uschar *subject, int options, int setup) +regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup) { int ovector[3*(EXPAND_MAXN+1)]; -int n = pcre_exec(re, NULL, CS subject, Ustrlen(subject), 0, +uschar * s = string_copy(subject); /* de-constifying */ +int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0, PCRE_EOPT | options, ovector, sizeof(ovector)/sizeof(int)); BOOL yield = n >= 0; if (n == 0) n = EXPAND_MAXN + 1; @@ -137,7 +145,7 @@ expand_nmax = (setup < 0)? 0 : setup + 1; for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2) { - expand_nstring[expand_nmax] = subject + ovector[nn]; + expand_nstring[expand_nmax] = s + ovector[nn]; expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn]; } expand_nmax--; @@ -267,6 +275,10 @@ when a bug in a function that calls milliwait() caused it to pass invalid data. That's when I added the check. :-) +We assume it to be not worth sleeping for under 100us; this value will +require revisiting as hardware advances. This avoids the issue of +a zero-valued timer setting meaning "never fire". + Argument: an itimerval structure containing the interval Returns: nothing */ @@ -276,6 +288,9 @@ { sigset_t sigmask; sigset_t old_sigmask; + +if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0) + return; (void)sigemptyset(&sigmask); /* Empty mask */ (void)sigaddset(&sigmask, SIGALRM); /* Add SIGALRM */ (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask); /* Block SIGALRM */ @@ -398,11 +413,11 @@ { if (!running_in_test_harness) { - debug_printf("tick check: %lu.%06lu %lu.%06lu\n", + debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n", then_tv->tv_sec, (long) then_tv->tv_usec, now_tv.tv_sec, (long) now_tv.tv_usec); - debug_printf("waiting %lu.%06lu\n", itval.it_value.tv_sec, - (long) itval.it_value.tv_usec); + debug_printf("waiting " TIME_T_FMT ".%06lu\n", + itval.it_value.tv_sec, (long) itval.it_value.tv_usec); } } @@ -805,6 +820,9 @@ #ifdef WITH_OLD_DEMIME fprintf(f, " Old_Demime"); #endif +#ifndef DISABLE_DNSSEC + fprintf(f, " DNSSEC"); +#endif #ifndef DISABLE_PRDR fprintf(f, " PRDR"); #endif @@ -820,6 +838,9 @@ #ifdef EXPERIMENTAL_BRIGHTMAIL fprintf(f, " Experimental_Brightmail"); #endif +#ifdef EXPERIMENTAL_DANE + fprintf(f, " Experimental_DANE"); +#endif #ifdef EXPERIMENTAL_DCC fprintf(f, " Experimental_DCC"); #endif @@ -829,17 +850,17 @@ #ifdef EXPERIMENTAL_PROXY fprintf(f, " Experimental_Proxy"); #endif -#ifdef EXPERIMENTAL_TPDA - fprintf(f, " Experimental_TPDA"); +#ifdef EXPERIMENTAL_EVENT + fprintf(f, " Experimental_Event"); #endif #ifdef EXPERIMENTAL_REDIS fprintf(f, " Experimental_Redis"); #endif -#ifdef EXPERIMENTAL_CERTNAMES - fprintf(f, " Experimental_Certnames"); +#ifdef EXPERIMENTAL_SOCKS + fprintf(f, " Experimental_SOCKS"); #endif -#ifdef EXPERIMENTAL_DSN - fprintf(f, " Experimental_DSN"); +#ifdef EXPERIMENTAL_INTERNATIONAL + fprintf(f, " Experimental_International"); #endif fprintf(f, "\n"); @@ -916,6 +937,9 @@ #ifdef AUTH_SPA fprintf(f, " spa"); #endif +#ifdef AUTH_TLS + fprintf(f, " tls"); +#endif fprintf(f, "\n"); fprintf(f, "Routers:"); @@ -1004,12 +1028,13 @@ #ifdef SUPPORT_TLS tls_version_report(f); #endif +#ifdef EXPERIMENTAL_INTERNATIONAL + utf8_version_report(f); +#endif - for (authi = auths_available; *authi->driver_name != '\0'; ++authi) { - if (authi->version_report) { + for (authi = auths_available; *authi->driver_name != '\0'; ++authi) + if (authi->version_report) (*authi->version_report)(f); - } - } /* PCRE_PRERELEASE is either defined and empty or a bare sequence of characters; unless it's an ancient version of PCRE in which case it @@ -1029,10 +1054,8 @@ init_lookup_list(); for (i = 0; i < lookup_list_count; i++) - { if (lookup_list[i]->version_report) lookup_list[i]->version_report(f); - } #ifdef WHITELIST_D_MACROS fprintf(f, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS); @@ -1489,6 +1512,7 @@ uschar *ftest_localpart = NULL; uschar *ftest_prefix = NULL; uschar *ftest_suffix = NULL; +uschar *log_oneline = NULL; uschar *malware_test_file = NULL; uschar *real_sender_address; uschar *originator_home = US"/"; @@ -2295,7 +2319,7 @@ if (nr_configs) { int sep = 0; - uschar *list = argrest; + const uschar *list = argrest; uschar *filename; while (trusted_config && (filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) != NULL) @@ -2500,7 +2524,7 @@ case 'f': { - int start, end; + int dummy_start, dummy_end; uschar *errmess; if (*argrest == 0) { @@ -2508,9 +2532,7 @@ { badarg = TRUE; break; } } if (*argrest == 0) - { sender_address = string_sprintf(""); /* Ensure writeable memory */ - } else { uschar *temp = argrest + Ustrlen(argrest) - 1; @@ -2518,8 +2540,15 @@ if (temp >= argrest && *temp == '.') f_end_dot = TRUE; allow_domain_literals = TRUE; strip_trailing_dot = TRUE; - sender_address = parse_extract_address(argrest, &errmess, &start, &end, - &sender_address_domain, TRUE); +#ifdef EXPERIMENTAL_INTERNATIONAL + allow_utf8_domains = TRUE; +#endif + sender_address = parse_extract_address(argrest, &errmess, + &dummy_start, &dummy_end, &sender_address_domain, TRUE); +#ifdef EXPERIMENTAL_INTERNATIONAL + message_smtputf8 = string_is_utf8(sender_address); + allow_utf8_domains = FALSE; +#endif allow_domain_literals = FALSE; strip_trailing_dot = FALSE; if (sender_address == NULL) @@ -2663,15 +2692,13 @@ break; } - #ifdef EXPERIMENTAL_DSN /* -MCD: set the smtp_use_dsn flag; this indicates that the host that exim is connected to supports the esmtp extension DSN */ - else if (strcmp(argrest, "CD") == 0) + else if (Ustrcmp(argrest, "CD") == 0) { smtp_use_dsn = TRUE; break; } - #endif /* -MCP: set the smtp_use_pipelining flag; this is useful only when it preceded -MC (see above) */ @@ -3374,13 +3401,20 @@ case 'X': if (*argrest == '\0') - { if (++i >= argc) { fprintf(stderr, "exim: string expected after -X\n"); exit(EXIT_FAILURE); } - } + break; + + case 'z': + if (*argrest == '\0') + if (++i < argc) log_oneline = argv[i]; else + { + fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]); + exit(EXIT_FAILURE); + } break; /* All other initial characters are errors */ @@ -3681,6 +3715,10 @@ This needs to happen before we read the main configuration. */ init_lookup_list(); +#ifdef EXPERIMENTAL_INTERNATIONAL +if (running_in_test_harness) smtputf8_advertise_hosts = NULL; +#endif + /* Read the main runtime configuration data; this gives up if there is a failure. It leaves the configuration file open so that the subsequent configuration data for delivery can be read if needed. */ @@ -3823,6 +3861,17 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "syslog_processname is longer than 32 chars: aborting"); +if (log_oneline) + { + if (admin_user) + { + log_write(0, LOG_MAIN, "%s", log_oneline); + return EXIT_SUCCESS; + } + else + return EXIT_FAILURE; + } + /* In some operating systems, the environment variable TMPDIR controls where temporary files are created; Exim doesn't use these (apart from when delivering to MBX mailboxes), but called libraries such as DBM libraries may require them. @@ -3966,7 +4015,7 @@ for (i = 0; i < argc; i++) { int len = Ustrlen(argv[i]); - uschar *printing; + const uschar *printing; uschar *quote; if (p + len + 8 >= big_buffer + big_buffer_size) { @@ -3978,7 +4027,7 @@ printing = string_printing(argv[i]); if (printing[0] == 0) quote = US"\""; else { - uschar *pp = printing; + const uschar *pp = printing; quote = US""; while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; } } @@ -5041,6 +5090,9 @@ deliver_drop_privilege = TRUE; queue_smtp = FALSE; queue_smtp_domains = NULL; +#ifdef EXPERIMENTAL_INTERNATIONAL + message_utf8_downconvert = -1; /* convert-if-needed */ +#endif } @@ -5319,7 +5371,6 @@ if (recipients_max > 0 && ++rcount > recipients_max && !extract_recipients) - { if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: too many recipients\n"); @@ -5331,11 +5382,22 @@ moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)? errors_sender_rc : EXIT_FAILURE; } - } +#ifdef EXPERIMENTAL_INTERNATIONAL + { + BOOL b = allow_utf8_domains; + allow_utf8_domains = TRUE; +#endif recipient = parse_extract_address(s, &errmess, &start, &end, &domain, FALSE); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (string_is_utf8(recipient)) + message_smtputf8 = TRUE; + else + allow_utf8_domains = b; + } +#endif if (domain == 0 && !allow_unqualified_recipient) { recipient = NULL; @@ -5435,9 +5497,7 @@ return_path = string_copy(sender_address); } else - { printf("Return-path = %s\n", (return_path[0] == 0)? US"<>" : return_path); - } printf("Sender = %s\n", (sender_address[0] == 0)? US"<>" : sender_address); receive_add_recipient( diff -Nru exim4-4.84/src/exim_dbmbuild.c exim4-4.86~RC4/src/exim_dbmbuild.c --- exim4-4.84/src/exim_dbmbuild.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/exim_dbmbuild.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -88,10 +88,10 @@ */ int -string_interpret_escape(uschar **pp) +string_interpret_escape(const uschar **pp) { int ch; -uschar *p = *pp; +const uschar *p = *pp; ch = *(++p); if (isdigit(ch) && ch != '8' && ch != '9') { @@ -329,7 +329,7 @@ keystart = t; while (*s != 0 && *s != '\"') { - if (*s == '\\') *t++ = string_interpret_escape(&s); + if (*s == '\\') *t++ = string_interpret_escape((const uschar **)&s); else *t++ = *s; s++; } diff -Nru exim4-4.84/src/exim_dbutil.c exim4-4.86~RC4/src/exim_dbutil.c --- exim4-4.84/src/exim_dbutil.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/exim_dbutil.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -356,15 +356,19 @@ */ void * -dbfn_read_with_length(open_db *dbblock, uschar *key, int *length) +dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) { void *yield; EXIM_DATUM key_datum, result_datum; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; @@ -396,16 +400,20 @@ */ int -dbfn_write(open_db *dbblock, uschar *key, void *ptr, int length) +dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length) { EXIM_DATUM key_datum, value_datum; dbdata_generic *gptr = (dbdata_generic *)ptr; +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */ EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; EXIM_DATUM_DATA(value_datum) = CS ptr; EXIM_DATUM_SIZE(value_datum) = length; return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum); @@ -426,12 +434,16 @@ */ int -dbfn_delete(open_db *dbblock, uschar *key) +dbfn_delete(open_db *dbblock, const uschar *key) { +int klen = Ustrlen(key) + 1; +uschar * key_copy = store_get(klen); + +memcpy(key_copy, key, klen); EXIM_DATUM key_datum; EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */ -EXIM_DATUM_DATA(key_datum) = CS key; -EXIM_DATUM_SIZE(key_datum) = Ustrlen(key) + 1; +EXIM_DATUM_DATA(key_datum) = CS key_copy; +EXIM_DATUM_SIZE(key_datum) = klen; return EXIM_DBDEL(dbblock->dbptr, key_datum); } @@ -630,19 +642,6 @@ printf("\n"); } - /* Old-style domain record, without separate timestamps. This code can - eventually be thrown away, say in 5 years' time (it's now Feb 2003). */ - - else - { - printf("%s %s callout=%s postmaster=%s random=%s\n", - print_time(((dbdata_generic *)value)->time_stamp), - keybuffer, - print_cache(callout->result), - print_cache(callout->postmaster_result), - print_cache(callout->random_result)); - } - break; case type_ratelimit: diff -Nru exim4-4.84/src/exim.h exim4-4.86~RC4/src/exim.h --- exim4-4.84/src/exim.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/exim.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -418,7 +418,7 @@ here. */ #ifndef ICONV_ARG2_TYPE -# define ICONV_ARG2_TYPE const char ** +# define ICONV_ARG2_TYPE char ** #endif /* One OS uses a different type for the 5th argument of getsockopt */ @@ -591,4 +591,9 @@ #endif #endif +/* DANE w/o DNSSEC is useless */ +#if defined(EXPERIMENTAL_DANE) && defined(DISABLE_DNSSEC) + #undef DISABLE_DNSSEC +#endif + /* End of exim.h */ diff -Nru exim4-4.84/src/eximon.src exim4-4.86~RC4/src/eximon.src --- exim4-4.84/src/eximon.src 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/eximon.src 2015-06-27 17:01:28.000000000 +0200 @@ -4,7 +4,7 @@ # The build process concatenates on the front of this various settings from # os-specific files and from the user's configuration file. -# Copyright (c) 2004 - 2012 University of Cambridge. +# Copyright (c) 2004 - 2015 University of Cambridge. # See the file NOTICE for conditions of use and distribution. # Except when they appear in comments, the following placeholders in this @@ -79,11 +79,12 @@ # If log_file_path is "syslog" then logging is only to syslog, and the monitor # is unable to display a log tail unless EXIMON_LOG_FILE_PATH is set to tell -# it where the log data is. Otherwise, remove any occurrences of -# "syslog:" or ":syslog" (spaces allowed in various places) and look at the -# remainder of the entry. If it's null, the default is "mainlog" in the -# "log" directory in the spool directory. Otherwise, set the name from the -# given path. +# it where the log data is. If log_file_path is unset (i.e. empty) the default +# is "mainlog" in the "log" directory in the spool directory. Otherwise, +# remove any occurrences of "syslog:" or ":syslog" (spaces allowed in various +# places) and look at the remainder of the entry. If it's null, check whether +# LOG_FILE_NAME was set a compile time and contains a path. Otherwise fall +# back to the default path. if [ "$EXIMON_LOG_FILE_PATH" != "" ] ; then LOG_FILE_NAME="$EXIMON_LOG_FILE_PATH" @@ -94,6 +95,8 @@ echo MAIL.INFO syslog messages into a separate file, you can point eximon at echo that file with the EXIMON_LOG_FILE_PATH environment variable. echo \*\*\* +elif [ "$LOG_FILE_PATH" = "" ] ; then + LOG_FILE_NAME=$SPOOL_DIRECTORY/log/mainlog else LOG_FILE_NAME=`echo $LOG_FILE_PATH | \ sed -e 's/ *: *syslog *: */:/' \ @@ -101,7 +104,17 @@ -e 's/^ *syslog *: *//' \ -e 's/%s/main/'` if [ "$LOG_FILE_NAME" = "" ] ; then - LOG_FILE_NAME=$SPOOL_DIRECTORY/log/mainlog + COMPILETIMEDEFAULT=`$EXIM_PATH -C /dev/null -bP log_file_path | \ + sed -e 's/.*=[ ]*//' \ + -e 's/ *: *syslog *: */:/' \ + -e 's/ *: *syslog *$//' \ + -e 's/^ *syslog *: *//' \ + -e 's/%s/main/'` + if [ "$COMPILETIMEDEFAULT" != "" ] ; then + LOG_FILE_NAME="$COMPILETIMEDEFAULT" + else + LOG_FILE_NAME=$SPOOL_DIRECTORY/log/mainlog + fi fi fi diff -Nru exim4-4.84/src/expand.c exim4-4.86~RC4/src/expand.c --- exim4-4.84/src/expand.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/expand.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -13,8 +13,8 @@ /* Recursively called function */ -static uschar *expand_string_internal(uschar *, BOOL, uschar **, BOOL, BOOL, BOOL *); -static int_eximarith_t expanded_string_integer(uschar *, BOOL); +static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *); +static int_eximarith_t expanded_string_integer(const uschar *, BOOL); #ifdef STAND_ALONE #ifndef SUPPORT_CRYPTEQ @@ -94,10 +94,6 @@ -#ifndef nelements -# define nelements(arr) (sizeof(arr) / sizeof(*arr)) -#endif - /************************************************* * Local statics and tables * *************************************************/ @@ -109,11 +105,15 @@ US"acl", US"certextract", US"dlfunc", + US"env", US"extract", US"filter", US"hash", US"hmac", US"if", +#ifdef EXPERIMENTAL_INTERNATIONAL + US"imapfolder", +#endif US"length", US"listextract", US"lookup", @@ -127,6 +127,7 @@ US"reduce", US"run", US"sg", + US"sort", US"substr", US"tr" }; @@ -134,11 +135,15 @@ EITEM_ACL, EITEM_CERTEXTRACT, EITEM_DLFUNC, + EITEM_ENV, EITEM_EXTRACT, EITEM_FILTER, EITEM_HASH, EITEM_HMAC, EITEM_IF, +#ifdef EXPERIMENTAL_INTERNATIONAL + EITEM_IMAPFOLDER, +#endif EITEM_LENGTH, EITEM_LISTEXTRACT, EITEM_LOOKUP, @@ -152,6 +157,7 @@ EITEM_REDUCE, EITEM_RUN, EITEM_SG, + EITEM_SORT, EITEM_SUBSTR, EITEM_TR }; @@ -166,7 +172,14 @@ US"quote_local_part", US"reverse_ip", US"time_eval", - US"time_interval"}; + US"time_interval" +#ifdef EXPERIMENTAL_INTERNATIONAL + ,US"utf8_domain_from_alabel", + US"utf8_domain_to_alabel", + US"utf8_localpart_from_alabel", + US"utf8_localpart_to_alabel" +#endif + }; enum { EOP_FROM_UTF8, @@ -174,7 +187,14 @@ EOP_QUOTE_LOCAL_PART, EOP_REVERSE_IP, EOP_TIME_EVAL, - EOP_TIME_INTERVAL }; + EOP_TIME_INTERVAL +#ifdef EXPERIMENTAL_INTERNATIONAL + ,EOP_UTF8_DOMAIN_FROM_ALABEL, + EOP_UTF8_DOMAIN_TO_ALABEL, + EOP_UTF8_LOCALPART_FROM_ALABEL, + EOP_UTF8_LOCALPART_TO_ALABEL +#endif + }; static uschar *op_table_main[] = { US"address", @@ -215,7 +235,7 @@ US"utf8clean" }; enum { - EOP_ADDRESS = sizeof(op_table_underscore)/sizeof(uschar *), + EOP_ADDRESS = nelem(op_table_underscore), EOP_ADDRESSES, EOP_BASE62, EOP_BASE62D, @@ -444,6 +464,8 @@ { "caller_uid", vtype_uid, &real_uid }, { "compile_date", vtype_stringptr, &version_date }, { "compile_number", vtype_stringptr, &version_cnumber }, + { "config_dir", vtype_stringptr, &config_main_directory }, + { "config_file", vtype_stringptr, &config_main_filename }, { "csa_status", vtype_stringptr, &csa_status }, #ifdef EXPERIMENTAL_DCC { "dcc_header", vtype_stringptr, &dcc_header }, @@ -488,9 +510,18 @@ { "dnslist_value", vtype_stringptr, &dnslist_value }, { "domain", vtype_stringptr, &deliver_domain }, { "domain_data", vtype_stringptr, &deliver_domain_data }, +#ifdef EXPERIMENTAL_EVENT + { "event_data", vtype_stringptr, &event_data }, + + /*XXX want to use generic vars for as many of these as possible*/ + { "event_defer_errno", vtype_int, &event_defer_errno }, + + { "event_name", vtype_stringptr, &event_name }, +#endif { "exim_gid", vtype_gid, &exim_gid }, { "exim_path", vtype_stringptr, &exim_path }, { "exim_uid", vtype_uid, &exim_uid }, + { "exim_version", vtype_stringptr, &version_string }, #ifdef WITH_OLD_DEMIME { "found_extension", vtype_stringptr, &found_extension }, #endif @@ -501,6 +532,7 @@ { "host_data", vtype_stringptr, &host_data }, { "host_lookup_deferred",vtype_int, &host_lookup_deferred }, { "host_lookup_failed", vtype_int, &host_lookup_failed }, + { "host_port", vtype_int, &deliver_host_port }, { "inode", vtype_ino, &deliver_inode }, { "interface_address", vtype_stringptr, &interface_address }, { "interface_port", vtype_int, &interface_port }, @@ -535,6 +567,9 @@ { "message_id", vtype_stringptr, &message_id }, { "message_linecount", vtype_int, &message_linecount }, { "message_size", vtype_int, &message_size }, +#ifdef EXPERIMENTAL_INTERNATIONAL + { "message_smtputf8", vtype_bool, &message_smtputf8 }, +#endif #ifdef WITH_CONTENT_SCAN { "mime_anomaly_level", vtype_int, &mime_anomaly_level }, { "mime_anomaly_text", vtype_stringptr, &mime_anomaly_text }, @@ -611,6 +646,7 @@ { "sender_address_local_part", vtype_localpart, &sender_address }, { "sender_data", vtype_stringptr, &sender_data }, { "sender_fullhost", vtype_stringptr, &sender_fullhost }, + { "sender_helo_dnssec", vtype_bool, &sender_helo_dnssec }, { "sender_helo_name", vtype_stringptr, &sender_helo_name }, { "sender_host_address", vtype_stringptr, &sender_host_address }, { "sender_host_authenticated",vtype_stringptr, &sender_host_authenticated }, @@ -641,6 +677,7 @@ { "sn8", vtype_filter_int, &filter_sn[8] }, { "sn9", vtype_filter_int, &filter_sn[9] }, #ifdef WITH_CONTENT_SCAN + { "spam_action", vtype_stringptr, &spam_action }, { "spam_bar", vtype_stringptr, &spam_bar }, { "spam_report", vtype_stringptr, &spam_report }, { "spam_score", vtype_stringptr, &spam_score }, @@ -684,6 +721,9 @@ { "tls_out_bits", vtype_int, &tls_out.bits }, { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified }, { "tls_out_cipher", vtype_stringptr, &tls_out.cipher }, +#ifdef EXPERIMENTAL_DANE + { "tls_out_dane", vtype_bool, &tls_out.dane_verified }, +#endif { "tls_out_ocsp", vtype_int, &tls_out.ocsp }, { "tls_out_ourcert", vtype_cert, &tls_out.ourcert }, { "tls_out_peercert", vtype_cert, &tls_out.peercert }, @@ -691,6 +731,9 @@ #if defined(SUPPORT_TLS) { "tls_out_sni", vtype_stringptr, &tls_out.sni }, #endif +#ifdef EXPERIMENTAL_DANE + { "tls_out_tlsa_usage", vtype_int, &tls_out.tlsa_usage }, +#endif { "tls_peerdn", vtype_stringptr, &tls_in.peerdn }, /* mind the alphabetical order! */ #if defined(SUPPORT_TLS) @@ -705,18 +748,9 @@ { "tod_logfile", vtype_todlf, NULL }, { "tod_zone", vtype_todzone, NULL }, { "tod_zulu", vtype_todzulu, NULL }, -#ifdef EXPERIMENTAL_TPDA - { "tpda_defer_errno", vtype_int, &tpda_defer_errno }, - { "tpda_defer_errstr", vtype_stringptr, &tpda_defer_errstr }, - { "tpda_delivery_confirmation", vtype_stringptr, &tpda_delivery_confirmation }, - { "tpda_delivery_domain", vtype_stringptr, &tpda_delivery_domain }, - { "tpda_delivery_fqdn", vtype_stringptr, &tpda_delivery_fqdn }, - { "tpda_delivery_ip", vtype_stringptr, &tpda_delivery_ip }, - { "tpda_delivery_local_part",vtype_stringptr,&tpda_delivery_local_part }, - { "tpda_delivery_port", vtype_int, &tpda_delivery_port }, -#endif { "transport_name", vtype_stringptr, &transport_name }, { "value", vtype_stringptr, &lookup_value }, + { "verify_mode", vtype_stringptr, &verify_mode }, { "version_number", vtype_stringptr, &version_string }, { "warn_message_delay", vtype_stringptr, &warnmsg_delay }, { "warn_message_recipient",vtype_stringptr, &warnmsg_recipients }, @@ -726,7 +760,7 @@ { "warnmsg_recipients", vtype_stringptr, &warnmsg_recipients } }; -static int var_table_size = sizeof(var_table)/sizeof(var_entry); +static int var_table_size = nelem(var_table); static uschar var_buffer[256]; static BOOL malformed_header; @@ -913,7 +947,9 @@ #ifdef HAVE_ARC4RANDOM /* cryptographically strong randomness, common on *BSD platforms, not so much elsewhere. Alas. */ +#ifndef NOT_HAVE_ARC4RANDOM_STIR arc4random_stir(); +#endif #elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) #ifdef HAVE_SRANDOMDEV /* uses random(4) for seeding */ @@ -961,8 +997,8 @@ Ustrchr() yields non-NULL if the character is zero (which is not something I expected). */ -static uschar * -read_name(uschar *name, int max, uschar *s, uschar *extras) +static const uschar * +read_name(uschar *name, int max, const uschar *s, uschar *extras) { int ptr = 0; while (*s != 0 && (isalnum(*s) || Ustrchr(extras, *s) != NULL)) @@ -995,8 +1031,8 @@ Returns: a pointer to the first character after the header name */ -static uschar * -read_header_name(uschar *name, int max, uschar *s) +static const uschar * +read_header_name(uschar *name, int max, const uschar *s) { int prelen = Ustrchr(name, '_') - name + 1; int ptr = Ustrlen(name) - prelen; @@ -1033,6 +1069,14 @@ return s; } +static const uschar * +read_cnumber(int *n, const uschar *s) +{ +*n = 0; +while (isdigit(*s)) *n = *n * 10 + (*s++ - '0'); +return s; +} + /************************************************* @@ -1050,7 +1094,7 @@ */ static uschar * -expand_getkeyed(uschar *key, uschar *s) +expand_getkeyed(uschar *key, const uschar *s) { int length = Ustrlen(key); while (isspace(*s)) s++; @@ -1061,7 +1105,7 @@ { int dkeylength; uschar *data; - uschar *dkey = s; + const uschar *dkey = s; while (*s != 0 && *s != '=' && !isspace(*s)) s++; dkeylength = s - dkey; @@ -1172,17 +1216,17 @@ static uschar * -expand_getlistele(int field, uschar * list) +expand_getlistele(int field, const uschar * list) { -uschar * tlist= list; +const uschar * tlist= list; int sep= 0; uschar dummy; if(field<0) -{ + { for(field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++; sep= 0; -} + } if(field==0) return NULL; while(--field>0 && (string_nextinlist(&list, &sep, &dummy, 1))) ; return string_nextinlist(&list, &sep, NULL, 0); @@ -1241,7 +1285,7 @@ return tls_cert_ext_by_oid(*(void **)vp->value, field, 0); for(cp = certfields; - cp < certfields + nelements(certfields); + cp < certfields + nelem(certfields); cp++) if (Ustrncmp(cp->name, field, cp->namelen) == 0) { @@ -1411,7 +1455,7 @@ while (*s != 0) { - if (i == 0) i = sizeof(prime)/sizeof(int) - 1; + if (i == 0) i = nelem(prime) - 1; total += prime[i--] * (unsigned int)(*s++); } @@ -1924,11 +1968,11 @@ */ static int -read_subs(uschar **sub, int n, int m, uschar **sptr, BOOL skipping, +read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping, BOOL check_end, uschar *name, BOOL *resetok) { int i; -uschar *s = *sptr; +const uschar *s = *sptr; while (isspace(*s)) s++; for (i = 0; i < n; i++) @@ -2004,15 +2048,15 @@ eval_acl(uschar ** sub, int nsub, uschar ** user_msgp) { int i; -uschar *tmp; int sav_narg = acl_narg; int ret; +uschar * dummy_logmsg; extern int acl_where; -if(--nsub > sizeof(acl_arg)/sizeof(*acl_arg)) nsub = sizeof(acl_arg)/sizeof(*acl_arg); +if(--nsub > nelem(acl_arg)) nsub = nelem(acl_arg); for (i = 0; i < nsub && sub[i+1]; i++) { - tmp = acl_arg[i]; + uschar * tmp = acl_arg[i]; acl_arg[i] = sub[i+1]; /* place callers args in the globals */ sub[i+1] = tmp; /* stash the old args using our caller's storage */ } @@ -2029,7 +2073,7 @@ acl_narg>0 ? acl_arg[0] : US"", acl_narg>1 ? " +more" : ""); -ret = acl_eval(acl_where, sub[0], user_msgp, &tmp); +ret = acl_eval(acl_where, sub[0], user_msgp, &dummy_logmsg); for (i = 0; i < nsub; i++) acl_arg[i] = sub[i+1]; /* restore old args */ @@ -2060,8 +2104,8 @@ NULL after an error */ -static uschar * -eval_condition(uschar *s, BOOL *resetok, BOOL *yield) +static const uschar * +eval_condition(const uschar *s, BOOL *resetok, BOOL *yield) { BOOL testfor = TRUE; BOOL tempcond, combined_cond; @@ -2071,7 +2115,7 @@ int_eximarith_t num[2]; struct stat statbuf; uschar name[256]; -uschar *sub[10]; +const uschar *sub[10]; const pcre *re; const uschar *rerror; @@ -2111,7 +2155,7 @@ /* Find which condition we are dealing with, and switch on it */ -cond_type = chop_match(name, cond_table, sizeof(cond_table)/sizeof(uschar *)); +cond_type = chop_match(name, cond_table, nelem(cond_table)); switch(cond_type) { /* def: tests for a non-empty variable, or for the existence of a header. If @@ -2291,6 +2335,7 @@ case ECOND_ACL: /* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} */ { + uschar *sub[10]; uschar *user_msg; BOOL cond = FALSE; int size = 0; @@ -2299,7 +2344,7 @@ while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; /*}*/ - switch(read_subs(sub, sizeof(sub)/sizeof(*sub), 1, + switch(read_subs(sub, nelem(sub), 1, &s, yield == NULL, TRUE, US"acl", resetok)) { case 1: expand_string_message = US"too few arguments or bracketing " @@ -2309,7 +2354,7 @@ } *resetok = FALSE; - if (yield != NULL) switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + if (yield != NULL) switch(eval_acl(sub, nelem(sub), &user_msg)) { case OK: cond = TRUE; @@ -2341,29 +2386,32 @@ in their own set of braces. */ case ECOND_SASLAUTHD: - #ifndef CYRUS_SASLAUTHD_SOCKET - goto COND_FAILED_NOT_COMPILED; - #else - while (isspace(*s)) s++; - if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ - switch(read_subs(sub, 4, 2, &s, yield == NULL, TRUE, US"saslauthd", resetok)) +#ifndef CYRUS_SASLAUTHD_SOCKET + goto COND_FAILED_NOT_COMPILED; +#else { - case 1: expand_string_message = US"too few arguments or bracketing " - "error for saslauthd"; - case 2: - case 3: return NULL; - } - if (sub[2] == NULL) sub[3] = NULL; /* realm if no service */ - if (yield != NULL) - { - int rc; - rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3], - &expand_string_message); - if (rc == ERROR || rc == DEFER) return NULL; - *yield = (rc == OK) == testfor; + uschar *sub[4]; + while (isspace(*s)) s++; + if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ + switch(read_subs(sub, nelem(sub), 2, &s, yield == NULL, TRUE, US"saslauthd", + resetok)) + { + case 1: expand_string_message = US"too few arguments or bracketing " + "error for saslauthd"; + case 2: + case 3: return NULL; + } + if (sub[2] == NULL) sub[3] = NULL; /* realm if no service */ + if (yield != NULL) + { + int rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3], + &expand_string_message); + if (rc == ERROR || rc == DEFER) return NULL; + *yield = (rc == OK) == testfor; + } + return s; } - return s; - #endif /* CYRUS_SASLAUTHD_SOCKET */ +#endif /* CYRUS_SASLAUTHD_SOCKET */ /* symbolic operators for numeric and string comparison, and a number of @@ -2741,17 +2789,18 @@ case ECOND_INLIST: case ECOND_INLISTI: { + const uschar * list = sub[1]; int sep = 0; uschar *save_iterate_item = iterate_item; int (*compare)(const uschar *, const uschar *); + DEBUG(D_expand) debug_printf("condition: %s\n", name); + tempcond = FALSE; - if (cond_type == ECOND_INLISTI) - compare = strcmpic; - else - compare = (int (*)(const uschar *, const uschar *)) strcmp; + compare = cond_type == ECOND_INLISTI + ? strcmpic : (int (*)(const uschar *, const uschar *)) strcmp; - while ((iterate_item = string_nextinlist(&sub[1], &sep, NULL, 0)) != NULL) + while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0))) if (compare(sub[0], iterate_item) == 0) { tempcond = TRUE; @@ -2829,9 +2878,12 @@ case ECOND_FORALL: case ECOND_FORANY: { + const uschar * list; int sep = 0; uschar *save_iterate_item = iterate_item; + DEBUG(D_expand) debug_printf("condition: %s\n", name); + while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ sub[0] = expand_string_internal(s, TRUE, &s, (yield == NULL), TRUE, resetok); @@ -2866,7 +2918,8 @@ } if (yield != NULL) *yield = !testfor; - while ((iterate_item = string_nextinlist(&sub[0], &sep, NULL, 0)) != NULL) + list = sub[0]; + while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)) != NULL) { DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, iterate_item); if (!eval_condition(sub[1], resetok, &tempcond)) @@ -3084,11 +3137,11 @@ */ static int -process_yesno(BOOL skipping, BOOL yes, uschar *save_lookup, uschar **sptr, +process_yesno(BOOL skipping, BOOL yes, uschar *save_lookup, const uschar **sptr, uschar **yieldptr, int *sizeptr, int *ptrptr, uschar *type, BOOL *resetok) { int rc = 0; -uschar *s = *sptr; /* Local value */ +const uschar *s = *sptr; /* Local value */ uschar *sub1, *sub2; /* If there are no following strings, we substitute the contents of $value for @@ -3166,7 +3219,8 @@ else if (*s != '}') { uschar name[256]; - s = read_name(name, sizeof(name), s, US"_"); + /* deconst cast ok here as source is s anyway */ + s = US read_name(name, sizeof(name), s, US"_"); if (Ustrcmp(name, "fail") == 0) { if (!yes && !skipping) @@ -3735,14 +3789,14 @@ */ static uschar * -expand_string_internal(uschar *string, BOOL ket_ends, uschar **left, +expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left, BOOL skipping, BOOL honour_dollar, BOOL *resetok_p) { int ptr = 0; int size = Ustrlen(string)+ 64; int item_type; uschar *yield = store_get(size); -uschar *s = string; +const uschar *s = string; uschar *save_expand_nstring[EXPAND_MAXN+1]; int save_expand_nlength[EXPAND_MAXN+1]; BOOL resetok = TRUE; @@ -3770,7 +3824,7 @@ if (s[1] == 'N') { - uschar *t = s + 2; + const uschar * t = s + 2; for (s = t; *s != 0; s++) if (*s == '\\' && s[1] == 'N') break; yield = string_cat(yield, &size, &ptr, t, s - t); if (*s != 0) s += 2; @@ -3886,7 +3940,7 @@ if (isdigit(*s)) { int n; - s = read_number(&n, s); + s = read_cnumber(&n, s); if (n >= 0 && n <= expand_nmax) yield = string_cat(yield, &size, &ptr, expand_nstring[n], expand_nlength[n]); @@ -3907,7 +3961,7 @@ if (isdigit((*(++s)))) { int n; - s = read_number(&n, s); /*{*/ + s = read_cnumber(&n, s); /*{*/ if (*s++ != '}') { /*{*/ expand_string_message = US"} expected after number"; @@ -3930,7 +3984,7 @@ OK. */ s = read_name(name, sizeof(name), s, US"_-"); - item_type = chop_match(name, item_table, sizeof(item_table)/sizeof(uschar *)); + item_type = chop_match(name, item_table, nelem(item_table)); switch(item_type) { @@ -3949,7 +4003,8 @@ uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; - switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl", &resetok)) + switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, US"acl", + &resetok)) { case 1: goto EXPAND_FAILED_CURLY; case 2: @@ -3958,7 +4013,7 @@ if (skipping) continue; resetok = FALSE; - switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + switch(eval_acl(sub, nelem(sub), &user_msg)) { case OK: case FAIL: @@ -3984,7 +4039,7 @@ case EITEM_IF: { BOOL cond = FALSE; - uschar *next_s; + const uschar *next_s; int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); @@ -4024,6 +4079,43 @@ continue; } +#ifdef EXPERIMENTAL_INTERNATIONAL + case EITEM_IMAPFOLDER: + { /* ${imapfolder {name}{sep]{specials}} */ + uschar *sub_arg[3]; + uschar *encoded; + + switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name, + &resetok)) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + + if (sub_arg[1] == NULL) /* One argument */ + { + sub_arg[1] = US"/"; /* default separator */ + sub_arg[2] = NULL; + } + else if (Ustrlen(sub_arg[1]) != 1) + { + expand_string_message = + string_sprintf( + "IMAP folder separator must be one character, found \"%s\"", + sub_arg[1]); + goto EXPAND_FAILED; + } + + if (!(encoded = imap_utf7_encode(sub_arg[0], headers_charset, + sub_arg[1][0], sub_arg[2], &expand_string_message))) + goto EXPAND_FAILED; + if (!skipping) + yield = string_cat(yield, &size, &ptr, encoded, Ustrlen(encoded)); + continue; + } +#endif + /* Handle database lookups unless locked out. If "skipping" is TRUE, we are expanding an internal string that isn't actually going to be used. All we need to do is check the syntax, so don't do a lookup at all. Preserve the @@ -4036,7 +4128,8 @@ int stype, partial, affixlen, starflags; int expand_setup = 0; int nameptr = 0; - uschar *key, *filename, *affix; + uschar *key, *filename; + const uschar *affix; uschar *save_lookup_value = lookup_value; int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); @@ -4754,7 +4847,7 @@ { FILE *f; uschar *arg; - uschar **argv; + const uschar **argv; pid_t pid; int fd_in, fd_out; int lsize = 0; @@ -4792,7 +4885,7 @@ /* Create the child process, making it a group leader. */ - pid = child_open(argv, NULL, 0077, &fd_in, &fd_out, TRUE); + pid = child_open(USS argv, NULL, 0077, &fd_in, &fd_out, TRUE); if (pid < 0) { @@ -5112,7 +5205,7 @@ { int ovector[3*(EXPAND_MAXN+1)]; int n = pcre_exec(re, NULL, CS subject, slen, moffset + moffsetextra, - PCRE_EOPT | emptyopt, ovector, sizeof(ovector)/sizeof(int)); + PCRE_EOPT | emptyopt, ovector, nelem(ovector)); int nn; uschar *insert; @@ -5222,25 +5315,28 @@ while (len > 0 && isspace(p[len-1])) len--; p[len] = 0; - if (*p == 0 && !skipping) - { - expand_string_message = US"first argument of \"extract\" must " - "not be empty"; - goto EXPAND_FAILED; - } + if (!skipping) + { + if (*p == 0) + { + expand_string_message = US"first argument of \"extract\" must " + "not be empty"; + goto EXPAND_FAILED; + } - if (*p == '-') - { - field_number = -1; - p++; - } - while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0'; - if (*p == 0) - { - field_number *= x; - j = 3; /* Need 3 args */ - field_number_set = TRUE; - } + if (*p == '-') + { + field_number = -1; + p++; + } + while (*p != 0 && isdigit(*p)) x = x * 10 + *p++ - '0'; + if (*p == 0) + { + field_number *= x; + j = 3; /* Need 3 args */ + field_number_set = TRUE; + } + } } } else goto EXPAND_FAILED_CURLY; @@ -5451,7 +5547,7 @@ int sep = 0; int save_ptr = ptr; uschar outsep[2] = { '\0', '\0' }; - uschar *list, *expr, *temp; + const uschar *list, *expr, *temp; uschar *save_iterate_item = iterate_item; uschar *save_lookup_value = lookup_value; @@ -5464,11 +5560,12 @@ if (item_type == EITEM_REDUCE) { + uschar * t; while (isspace(*s)) s++; if (*s++ != '{') goto EXPAND_FAILED_CURLY; - temp = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); - if (temp == NULL) goto EXPAND_FAILED; - lookup_value = temp; + t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); + if (!t) goto EXPAND_FAILED; + lookup_value = t; if (*s++ != '}') goto EXPAND_FAILED_CURLY; } @@ -5489,9 +5586,7 @@ if (temp != NULL) s = temp; } else - { temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); - } if (temp == NULL) { @@ -5549,7 +5644,8 @@ else { - temp = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok); + uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok); + temp = t; if (temp == NULL) { iterate_item = save_iterate_item; @@ -5559,7 +5655,7 @@ } if (item_type == EITEM_REDUCE) { - lookup_value = temp; /* Update the value of $value */ + lookup_value = t; /* Update the value of $value */ continue; /* and continue the iteration */ } } @@ -5619,6 +5715,144 @@ continue; } + case EITEM_SORT: + { + int sep = 0; + const uschar *srclist, *cmp, *xtract; + uschar *srcitem; + const uschar *dstlist = NULL, *dstkeylist = NULL; + uschar * tmp; + uschar *save_iterate_item = iterate_item; + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); + if (!srclist) goto EXPAND_FAILED; + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok); + if (!cmp) goto EXPAND_FAILED; + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + while (isspace(*s)) s++; + if (*s++ != '{') goto EXPAND_FAILED_CURLY; + + xtract = s; + tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); + if (!tmp) goto EXPAND_FAILED; + xtract = string_copyn(xtract, s - xtract); + + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + /*{*/ + if (*s++ != '}') + { /*{*/ + expand_string_message = US"missing } at end of \"sort\""; + goto EXPAND_FAILED; + } + + if (skipping) continue; + + while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0))) + { + uschar * dstitem; + uschar * newlist = NULL; + uschar * newkeylist = NULL; + uschar * srcfield; + + DEBUG(D_expand) debug_printf("%s: $item = \"%s\"\n", name, srcitem); + + /* extract field for comparisons */ + iterate_item = srcitem; + if ( !(srcfield = expand_string_internal(xtract, FALSE, NULL, FALSE, + TRUE, &resetok)) + || !*srcfield) + { + expand_string_message = string_sprintf( + "field-extract in sort: \"%s\"", xtract); + goto EXPAND_FAILED; + } + + /* Insertion sort */ + + /* copy output list until new-item < list-item */ + while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) + { + uschar * dstfield; + uschar * expr; + BOOL before; + + /* field for comparison */ + if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) + goto sort_mismatch; + + /* build and run condition string */ + expr = string_sprintf("%s{%s}{%s}", cmp, srcfield, dstfield); + + DEBUG(D_expand) debug_printf("%s: cond = \"%s\"\n", name, expr); + if (!eval_condition(expr, &resetok, &before)) + { + expand_string_message = string_sprintf("comparison in sort: %s", + expr); + goto EXPAND_FAILED; + } + + if (before) + { + /* New-item sorts before this dst-item. Append new-item, + then dst-item, then remainder of dst list. */ + + newlist = string_append_listele(newlist, sep, srcitem); + newkeylist = string_append_listele(newkeylist, sep, srcfield); + srcitem = NULL; + + newlist = string_append_listele(newlist, sep, dstitem); + newkeylist = string_append_listele(newkeylist, sep, dstfield); + + while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) + { + if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) + goto sort_mismatch; + newlist = string_append_listele(newlist, sep, dstitem); + newkeylist = string_append_listele(newkeylist, sep, dstfield); + } + + break; + } + + newlist = string_append_listele(newlist, sep, dstitem); + newkeylist = string_append_listele(newkeylist, sep, dstfield); + } + + /* If we ran out of dstlist without consuming srcitem, append it */ + if (srcitem) + { + newlist = string_append_listele(newlist, sep, srcitem); + newkeylist = string_append_listele(newkeylist, sep, srcfield); + } + + dstlist = newlist; + dstkeylist = newkeylist; + + DEBUG(D_expand) debug_printf("%s: dstlist = \"%s\"\n", name, dstlist); + DEBUG(D_expand) debug_printf("%s: dstkeylist = \"%s\"\n", name, dstkeylist); + } + + if (dstlist) + yield = string_cat(yield, &size, &ptr, dstlist, Ustrlen(dstlist)); + + /* Restore preserved $item */ + iterate_item = save_iterate_item; + continue; + + sort_mismatch: + expand_string_message = US"Internal error in sort (list mismatch)"; + goto EXPAND_FAILED; + } + /* If ${dlfunc } support is configured, handle calling dynamically-loaded functions, unless locked out at this time. Syntax is ${dlfunc{file}{func}} @@ -5628,12 +5862,12 @@ #define EXPAND_DLFUNC_MAX_ARGS 8 case EITEM_DLFUNC: - #ifndef EXPAND_DLFUNC - expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/ - "is not included in this binary"; - goto EXPAND_FAILED; +#ifndef EXPAND_DLFUNC + expand_string_message = US"\"${dlfunc\" encountered, but this facility " /*}*/ + "is not included in this binary"; + goto EXPAND_FAILED; - #else /* EXPAND_DLFUNC */ +#else /* EXPAND_DLFUNC */ { tree_node *t; exim_dlfunc_t *func; @@ -5719,7 +5953,39 @@ goto EXPAND_FAILED; } } - #endif /* EXPAND_DLFUNC */ +#endif /* EXPAND_DLFUNC */ + + case EITEM_ENV: /* ${env {name} {val_if_found} {val_if_unfound}} */ + { + uschar * key; + uschar *save_lookup_value = lookup_value; + + while (isspace(*s)) s++; + if (*s != '{') /*}*/ + goto EXPAND_FAILED; + + key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + if (!key) goto EXPAND_FAILED; /*{*/ + if (*s++ != '}') goto EXPAND_FAILED_CURLY; + + lookup_value = US getenv(CS key); + + switch(process_yesno( + skipping, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + &size, /* output size */ + &ptr, /* output current point */ + US"env", /* condition type */ + &resetok)) + { + case 1: goto EXPAND_FAILED; /* when all is well, the */ + case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ + } + continue; + } } /* EITEM_* switch */ /* Control reaches here if the name is not recognized as one of the more @@ -5740,13 +6006,12 @@ the arguments and then scan the main table. */ if ((c = chop_match(name, op_table_underscore, - sizeof(op_table_underscore)/sizeof(uschar *))) < 0) + nelem(op_table_underscore))) < 0) { arg = Ustrchr(name, '_'); if (arg != NULL) *arg = 0; - c = chop_match(name, op_table_main, - sizeof(op_table_main)/sizeof(uschar *)); - if (c >= 0) c += sizeof(op_table_underscore)/sizeof(uschar *); + c = chop_match(name, op_table_main, nelem(op_table_main)); + if (c >= 0) c += nelem(op_table_underscore); if (arg != NULL) *arg++ = '_'; /* Put back for error messages */ } @@ -5760,7 +6025,7 @@ case EOP_SHA256: if (s[1] == '$') { - uschar * s1 = s; + const uschar * s1 = s; sub = expand_string_internal(s+2, TRUE, &s1, skipping, FALSE, &resetok); if (!sub) goto EXPAND_FAILED; /*{*/ @@ -5991,7 +6256,7 @@ uschar * cp; uschar buffer[256]; - while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++; + while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++; cp = string_sprintf("%d", cnt); yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp)); continue; @@ -6003,7 +6268,7 @@ case EOP_LISTNAMED: { tree_node *t = NULL; - uschar * list; + const uschar * list; int sep = 0; uschar * item; uschar * suffix = US""; @@ -6322,7 +6587,7 @@ case EOP_RFC2047: { uschar buffer[2048]; - uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset, + const uschar *string = parse_quote_2047(sub, Ustrlen(sub), headers_charset, buffer, sizeof(buffer), FALSE); yield = string_cat(yield, &size, &ptr, string, Ustrlen(string)); continue; @@ -6368,39 +6633,33 @@ case EOP_UTF8CLEAN: { - int seq_len, index = 0; + int seq_len = 0, index = 0; int bytes_left = 0; + long codepoint = -1; uschar seq_buff[4]; /* accumulate utf-8 here */ while (*sub != 0) { - int complete; - long codepoint; - uschar c; + int complete = 0; + uschar c = *sub++; - complete = 0; - c = *sub++; if (bytes_left) { if ((c & 0xc0) != 0x80) - { /* wrong continuation byte; invalidate all bytes */ complete = 1; /* error */ - } else { codepoint = (codepoint << 6) | (c & 0x3f); seq_buff[index++] = c; if (--bytes_left == 0) /* codepoint complete */ - { if(codepoint > 0x10FFFF) /* is it too large? */ - complete = -1; /* error */ + complete = -1; /* error (RFC3629 limit) */ else { /* finished; output utf-8 sequence */ yield = string_cat(yield, &size, &ptr, seq_buff, seq_len); index = 0; } - } } } else /* no bytes left: new sequence */ @@ -6443,18 +6702,80 @@ yield = string_cat(yield, &size, &ptr, UTF8_REPLACEMENT_CHAR, 1); } if ((complete == 1) && ((c & 0x80) == 0)) - { /* ASCII character follows incomplete sequence */ + /* ASCII character follows incomplete sequence */ yield = string_cat(yield, &size, &ptr, &c, 1); - } } continue; } +#ifdef EXPERIMENTAL_INTERNATIONAL + case EOP_UTF8_DOMAIN_TO_ALABEL: + { + uschar * error = NULL; + uschar * s = string_domain_utf8_to_alabel(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting utf8 (%s) to alabel: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } + + case EOP_UTF8_DOMAIN_FROM_ALABEL: + { + uschar * error = NULL; + uschar * s = string_domain_alabel_to_utf8(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting alabel (%s) to utf8: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } + + case EOP_UTF8_LOCALPART_TO_ALABEL: + { + uschar * error = NULL; + uschar * s = string_localpart_utf8_to_alabel(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting utf8 (%s) to alabel: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + DEBUG(D_expand) debug_printf("yield: '%s'\n", yield); + continue; + } + + case EOP_UTF8_LOCALPART_FROM_ALABEL: + { + uschar * error = NULL; + uschar * s = string_localpart_alabel_to_utf8(sub, &error); + if (error) + { + expand_string_message = string_sprintf( + "error converting alabel (%s) to utf8: %s", + string_printing(sub), error); + goto EXPAND_FAILED; + } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + continue; + } +#endif /* EXPERIMENTAL_INTERNATIONAL */ + /* escape turns all non-printing characters into escape sequences. */ case EOP_ESCAPE: { - uschar *t = string_printing(sub); + const uschar *t = string_printing(sub); yield = string_cat(yield, &size, &ptr, t, Ustrlen(t)); continue; } @@ -6842,23 +7163,35 @@ +const uschar * +expand_cstring(const uschar *string) +{ +search_find_defer = FALSE; +malformed_header = FALSE; +return (Ustrpbrk(string, "$\\") == NULL)? string : + expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL); +} + + + /************************************************* * Expand and copy * *************************************************/ /* Now and again we want to expand a string and be sure that the result is in a new bit of store. This function does that. +Since we know it has been copied, the de-const cast is safe. Argument: the string to be expanded Returns: the expanded string, always in a new bit of store, or NULL */ uschar * -expand_string_copy(uschar *string) +expand_string_copy(const uschar *string) { -uschar *yield = expand_string(string); +const uschar *yield = expand_cstring(string); if (yield == string) yield = string_copy(string); -return yield; +return US yield; } @@ -6905,7 +7238,7 @@ */ static int_eximarith_t -expanded_string_integer(uschar *s, BOOL isplus) +expanded_string_integer(const uschar *s, BOOL isplus) { int_eximarith_t value; uschar *msg = US"invalid integer \"%s\""; @@ -6984,6 +7317,67 @@ } +/* These values are usually fixed boolean values, but they are permitted to be +expanded strings. + +Arguments: + addr address being routed + mtype the module type + mname the module name + dbg_opt debug selectors + oname the option name + bvalue the router's boolean value + svalue the router's string value + rvalue where to put the returned value + +Returns: OK value placed in rvalue + DEFER expansion failed +*/ + +int +exp_bool(address_item *addr, + uschar *mtype, uschar *mname, unsigned dbg_opt, + uschar *oname, BOOL bvalue, + uschar *svalue, BOOL *rvalue) +{ +uschar *expanded; +if (svalue == NULL) { *rvalue = bvalue; return OK; } + +expanded = expand_string(svalue); +if (expanded == NULL) + { + if (expand_string_forcedfail) + { + DEBUG(dbg_opt) debug_printf("expansion of \"%s\" forced failure\n", oname); + *rvalue = bvalue; + return OK; + } + addr->message = string_sprintf("failed to expand \"%s\" in %s %s: %s", + oname, mname, mtype, expand_string_message); + DEBUG(dbg_opt) debug_printf("%s\n", addr->message); + return DEFER; + } + +DEBUG(dbg_opt) debug_printf("expansion of \"%s\" yields \"%s\"\n", oname, + expanded); + +if (strcmpic(expanded, US"true") == 0 || strcmpic(expanded, US"yes") == 0) + *rvalue = TRUE; +else if (strcmpic(expanded, US"false") == 0 || strcmpic(expanded, US"no") == 0) + *rvalue = FALSE; +else + { + addr->message = string_sprintf("\"%s\" is not a valid value for the " + "\"%s\" option in the %s %s", expanded, oname, mname, mtype); + return DEFER; + } + +return OK; +} + + + + /************************************************* ************************************************** * Stand-alone test program * @@ -6998,7 +7392,7 @@ { int ovector[3*(EXPAND_MAXN+1)]; int n = pcre_exec(re, NULL, subject, Ustrlen(subject), 0, PCRE_EOPT|options, - ovector, sizeof(ovector)/sizeof(int)); + ovector, nelem(ovector)); BOOL yield = n >= 0; if (n == 0) n = EXPAND_MAXN + 1; if (yield) diff -Nru exim4-4.84/src/filter.c exim4-4.86~RC4/src/filter.c --- exim4-4.84/src/filter.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/filter.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -355,7 +355,7 @@ } } - *bp++ = string_interpret_escape(&ptr); + *bp++ = string_interpret_escape(CUSS &ptr); } } @@ -1821,7 +1821,7 @@ set in a system filter and to the local address in user filters. */ addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */ - addr->p.errors_address = (s == NULL)? + addr->prop.errors_address = (s == NULL)? s : string_copy(s); /* Default is NULL */ if (commands->noerror) setflag(addr, af_ignore_error); addr->next = *generated; @@ -2021,7 +2021,7 @@ { int sep = 0; uschar *ss; - uschar *list = s; + const uschar *list = s; uschar buffer[128]; while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) @@ -2057,7 +2057,7 @@ DEFERFREEZEFAIL: fmsg = expargs[0]; if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, " ... (truncated)"); - fmsg = string_printing(fmsg); + fmsg = US string_printing(fmsg); *error_pointer = fmsg; if (filter_test != FTEST_NONE) @@ -2351,7 +2351,7 @@ case testprint_command: if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) { - uschar *s = string_printing(expargs[0]); + const uschar *s = string_printing(expargs[0]); if (filter_test == FTEST_NONE) debug_printf("Filter: testprint: %s\n", s); else diff -Nru exim4-4.84/src/functions.h exim4-4.86~RC4/src/functions.h --- exim4-4.84/src/functions.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/functions.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -44,12 +44,16 @@ extern uschar * tls_cert_fprt_sha256(void *); extern int tls_client_start(int, host_item *, address_item *, - void *); + transport_instance * +# ifdef EXPERIMENTAL_DANE + , dns_answer * +# endif + ); extern void tls_close(BOOL, BOOL); extern int tls_export_cert(uschar *, size_t, void *); extern int tls_feof(void); extern int tls_ferror(void); -extern void tls_free_cert(void *); +extern void tls_free_cert(void **); extern int tls_getc(void); extern int tls_import_cert(const uschar *, void **); extern int tls_read(BOOL, uschar *, size_t); @@ -62,10 +66,13 @@ # ifndef USE_GNUTLS extern BOOL tls_openssl_options_parse(uschar *, long *); # endif -extern uschar * tls_field_from_dn(uschar *, uschar *); -# ifdef EXPERIMENTAL_CERTNAMES -extern BOOL tls_is_name_for_cert(uschar *, void *); +extern uschar * tls_field_from_dn(uschar *, const uschar *); +extern BOOL tls_is_name_for_cert(const uschar *, void *); + +# ifdef EXPERIMENTAL_DANE +extern int tlsa_lookup(const host_item *, dns_answer *, BOOL, BOOL *); # endif + #endif /*SUPPORT_TLS*/ @@ -79,11 +86,11 @@ extern void acl_var_write(uschar *, uschar *, void *); extern uschar *auth_b64encode(uschar *, int); extern int auth_b64decode(uschar *, uschar **); -extern int auth_call_pam(uschar *, uschar **); +extern int auth_call_pam(const uschar *, uschar **); extern int auth_call_pwcheck(uschar *, uschar **); -extern int auth_call_radius(uschar *, uschar **); -extern int auth_call_saslauthd(uschar *, uschar *, uschar *, uschar *, - uschar **); +extern int auth_call_radius(const uschar *, uschar **); +extern int auth_call_saslauthd(const uschar *, const uschar *, + const uschar *, const uschar *, uschar **); extern int auth_check_serv_cond(auth_instance *); extern int auth_check_some_cond(auth_instance *, uschar *, uschar *, int); @@ -93,10 +100,10 @@ extern int auth_xtextdecode(uschar *, uschar **); extern void cancel_cutthrough_connection(const char *); -extern int check_host(void *, uschar *, uschar **, uschar **); +extern int check_host(void *, const uschar *, const uschar **, uschar **); extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...); -extern pid_t child_open_uid(uschar **, uschar **, int, uid_t *, gid_t *, - int *, int *, uschar *, BOOL); +extern pid_t child_open_uid(const uschar **, const uschar **, int, + uid_t *, gid_t *, int *, int *, uschar *, BOOL); extern uschar *cutthrough_finaldot(void); extern BOOL cutthrough_flush_send(void); extern BOOL cutthrough_headers_send(void); @@ -111,7 +118,7 @@ #endif extern void debug_logging_activate(uschar *, uschar *); -extern void debug_print_argv(uschar **); +extern void debug_print_argv(const uschar **); extern void debug_print_ids(uschar *); extern void debug_print_string(uschar *); extern void debug_print_tree(tree_node *); @@ -119,12 +126,16 @@ extern void decode_bits(unsigned int *, unsigned int *, int, int, uschar *, bit_table *, int, uschar *, int); extern address_item *deliver_make_addr(uschar *, BOOL); +extern void deliver_init(void); extern void delivery_log(int, address_item *, int, uschar *); extern int deliver_message(uschar *, BOOL, BOOL); extern void deliver_msglog(const char *, ...) PRINTF_FUNCTION(1,2); extern void deliver_set_expansions(address_item *); extern int deliver_split_address(address_item *); extern void deliver_succeeded(address_item *); + +extern uschar *deliver_get_sender_address (uschar *id); + #ifdef WITH_OLD_DEMIME extern int demime(uschar **); #endif @@ -135,12 +146,14 @@ int, uschar *, uschar *, uschar *, uschar *, uschar *, uschar *); #endif extern dns_address *dns_address_from_rr(dns_answer *, dns_record *); -extern void dns_build_reverse(uschar *, uschar *); +extern int dns_basic_lookup(dns_answer *, const uschar *, int); +extern void dns_build_reverse(const uschar *, uschar *); extern void dns_init(BOOL, BOOL, BOOL); -extern int dns_basic_lookup(dns_answer *, uschar *, int); -extern BOOL dns_is_secure(dns_answer *); -extern int dns_lookup(dns_answer *, uschar *, int, uschar **); -extern int dns_special_lookup(dns_answer *, uschar *, int, uschar **); +extern BOOL dns_is_aa(const dns_answer *); +extern BOOL dns_is_secure(const dns_answer *); +extern int dns_lookup(dns_answer *, const uschar *, int, const uschar **); +extern void dns_pattern_init(void); +extern int dns_special_lookup(dns_answer *, const uschar *, int, const uschar **); extern dns_record *dns_next_rr(dns_answer *, dns_scan *, int); extern uschar *dns_text_type(int); extern void dscp_list_to_stream(FILE *); @@ -148,17 +161,26 @@ extern void enq_end(uschar *); extern BOOL enq_start(uschar *); +#ifdef EXPERIMENTAL_EVENT +extern uschar *event_raise(uschar *, const uschar *, uschar *); +#endif extern void exim_exit(int); extern void exim_nullstd(void); extern void exim_setugid(uid_t, gid_t, BOOL, uschar *); extern int exim_tvcmp(struct timeval *, struct timeval *); extern void exim_wait_tick(struct timeval *, int); +extern int exp_bool(address_item *addr, + uschar *mtype, uschar *mname, unsigned dgb_opt, uschar *oname, BOOL bvalue, + uschar *svalue, BOOL *rvalue); extern BOOL expand_check_condition(uschar *, uschar *, uschar *); -extern uschar *expand_string(uschar *); -extern uschar *expand_string_copy(uschar *); +extern uschar *expand_string(uschar *); /* public, cannot make const */ +extern const uschar *expand_cstring(const uschar *); /* ... so use this one */ +extern uschar *expand_string_copy(const uschar *); extern int_eximarith_t expand_string_integer(uschar *, BOOL); extern void modify_variable(uschar *, void *); +extern BOOL fd_ready(int, int); + extern int filter_interpret(uschar *, int, address_item **, uschar **); extern BOOL filter_personal(string_item *, BOOL); extern BOOL filter_runtest(int, uschar *, BOOL, BOOL); @@ -171,16 +193,17 @@ extern BOOL header_match(uschar *, BOOL, BOOL, string_item *, int, ...); extern int host_address_extract_port(uschar *); extern uschar *host_and_ident(BOOL); -extern int host_aton(uschar *, int *); -extern void host_build_hostlist(host_item **, uschar *, BOOL); -extern ip_address_item *host_build_ifacelist(uschar *, uschar *); +extern int host_aton(const uschar *, int *); +extern void host_build_hostlist(host_item **, const uschar *, BOOL); +extern ip_address_item *host_build_ifacelist(const uschar *, uschar *); extern void host_build_log_info(void); extern void host_build_sender_fullhost(void); -extern BOOL host_find_byname(host_item *, uschar *, int, uschar **, BOOL); -extern int host_find_bydns(host_item *, uschar *, int, uschar *, uschar *, - uschar *, uschar *, uschar *, uschar **, BOOL *); +extern BOOL host_find_byname(host_item *, const uschar *, int, + const uschar **, BOOL); +extern int host_find_bydns(host_item *, const uschar *, int, uschar *, uschar *, + uschar *, const dnssec_domains *, const uschar **, BOOL *); extern ip_address_item *host_find_interfaces(void); -extern BOOL host_is_in_net(uschar *, uschar *, int); +extern BOOL host_is_in_net(const uschar *, const uschar *, int); extern BOOL host_is_tls_on_connect_port(int); extern int host_item_get_port(host_item *); extern void host_mask(int, int *, int); @@ -189,34 +212,43 @@ extern uschar *host_ntoa(int, const void *, uschar *, int *); extern int host_scan_for_local_hosts(host_item *, host_item **, BOOL *); +extern uschar *imap_utf7_encode(uschar *, const uschar *, + uschar, uschar *, uschar **); + extern void invert_address(uschar *, uschar *); +extern int ip_addr(void *, int, const uschar *, int); extern int ip_bind(int, int, uschar *, int); -extern int ip_connect(int, int, uschar *, int, int); +extern int ip_connect(int, int, const uschar *, int, int); extern int ip_connectedsocket(int, const uschar *, int, int, int, host_item *, uschar **); extern int ip_get_address_family(int); -extern void ip_keepalive(int, uschar *, BOOL); +extern void ip_keepalive(int, const uschar *, BOOL); extern int ip_recv(int, uschar *, int, int); extern int ip_socket(int, int); +extern int ip_tcpsocket(const uschar *, uschar **, int); +extern int ip_unixsocket(const uschar *, uschar **); +extern int ip_streamsocket(const uschar *, uschar **, int); + extern uschar *local_part_quote(uschar *); extern int log_create(uschar *); extern int log_create_as_exim(uschar *); extern void log_close_all(void); #ifdef WITH_CONTENT_SCAN -extern int malware(uschar **); +extern int malware(const uschar *, int); extern int malware_in_file(uschar *); +extern void malware_init(void); #endif -extern int match_address_list(uschar *, BOOL, BOOL, uschar **, - unsigned int *, int, int, uschar **); -extern int match_check_list(uschar **, int, tree_node **, unsigned int **, - int(*)(void *, uschar *, uschar **, uschar **), void *, int, - uschar *, uschar **); -extern int match_isinlist(uschar *, uschar **, int, tree_node **, - unsigned int *, int, BOOL, uschar **); -extern int match_check_string(uschar *, uschar *, int, BOOL, BOOL, BOOL, - uschar **); +extern int match_address_list(const uschar *, BOOL, BOOL, const uschar **, + unsigned int *, int, int, const uschar **); +extern int match_check_list(const uschar **, int, tree_node **, unsigned int **, + int(*)(void *, const uschar *, const uschar **, uschar **), void *, int, + const uschar *, const uschar **); +extern int match_isinlist(const uschar *, const uschar **, int, tree_node **, + unsigned int *, int, BOOL, const uschar **); +extern int match_check_string(const uschar *, const uschar *, int, BOOL, BOOL, BOOL, + const uschar **); extern void md5_end(md5 *, const uschar *, int, uschar *); extern void md5_mid(md5 *, const uschar *); extern void md5_start(md5 *); @@ -225,8 +257,8 @@ struct mime_boundary_context; extern int mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *, uschar **, uschar **); -extern int mime_decode(uschar **); -extern int mime_regex(uschar **); +extern int mime_decode(const uschar **); +extern int mime_regex(const uschar **); #endif extern uschar *moan_check_errorcopy(uschar *); extern BOOL moan_skipped_syntax_errors(uschar *, error_block *, uschar *, @@ -243,12 +275,12 @@ extern uschar *parse_extract_address(uschar *, uschar **, int *, int *, int *, BOOL); extern int parse_forward_list(uschar *, int, address_item **, uschar **, - uschar *, uschar *, error_block **); + const uschar *, uschar *, error_block **); extern uschar *parse_find_address_end(uschar *, BOOL); extern uschar *parse_find_at(uschar *); -extern uschar *parse_fix_phrase(uschar *, int, uschar *, int); +extern const uschar *parse_fix_phrase(const uschar *, int, uschar *, int); extern uschar *parse_message_id(uschar *, uschar **, uschar **); -extern uschar *parse_quote_2047(uschar *, int, uschar *, uschar *, int, BOOL); +extern const uschar *parse_quote_2047(const uschar *, int, uschar *, uschar *, int, BOOL); extern uschar *parse_date_time(uschar *str, time_t *t); extern int vaguely_random_number(int); #ifdef SUPPORT_TLS @@ -262,9 +294,6 @@ extern void queue_run(uschar *, uschar *, BOOL); extern int random_number(int); -#ifdef WITH_CONTENT_SCAN -extern int recv_line(int, uschar *, int); -#endif extern int rda_interpret(redirect_block *, int, uschar *, uschar *, uschar *, uschar *, uschar *, ugid_block *, address_item **, uschar **, error_block **, int *, uschar *); @@ -277,9 +306,9 @@ extern void readconf_print(uschar *, uschar *, BOOL); extern uschar *readconf_printtime(int); extern uschar *readconf_readname(uschar *, int, uschar *); -extern int readconf_readtime(uschar *, int, BOOL); +extern int readconf_readtime(const uschar *, int, BOOL); extern void readconf_rest(); -extern uschar *readconf_retry_error(uschar *, uschar *, int *, int *); +extern uschar *readconf_retry_error(const uschar *, const uschar *, int *, int *); extern void read_message_body(BOOL); extern void receive_bomb_out(uschar *, uschar *); extern BOOL receive_check_fs(int); @@ -288,20 +317,21 @@ extern int receive_statvfs(BOOL, int *); extern void receive_swallow_smtp(void); #ifdef WITH_CONTENT_SCAN -extern int regex(uschar **); +extern int regex(const uschar **); #endif -extern BOOL regex_match_and_setup(const pcre *, uschar *, int, int); -extern const pcre *regex_must_compile(uschar *, BOOL, BOOL); +extern BOOL regex_match_and_setup(const pcre *, const uschar *, int, int); +extern const pcre *regex_must_compile(const uschar *, BOOL, BOOL); extern void retry_add_item(address_item *, uschar *, int); -extern BOOL retry_check_address(uschar *, host_item *, uschar *, BOOL, +extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, uschar **, uschar **); -extern retry_config *retry_find_config(uschar *, uschar *, int, int); -extern BOOL retry_ultimate_address_timeout(uschar *, uschar *, +extern retry_config *retry_find_config(const uschar *, const uschar *, int, int); +extern BOOL retry_ultimate_address_timeout(uschar *, const uschar *, dbdata_retry *, time_t); extern void retry_update(address_item **, address_item **, address_item **); extern uschar *rewrite_address(uschar *, BOOL, BOOL, rewrite_rule *, int); extern uschar *rewrite_address_qualify(uschar *, BOOL); -extern header_line *rewrite_header(header_line *, uschar *, uschar *, +extern header_line *rewrite_header(header_line *, + const uschar *, const uschar *, rewrite_rule *, int, BOOL); extern uschar *rewrite_one(uschar *, int, BOOL *, BOOL, uschar *, rewrite_rule *); @@ -310,10 +340,10 @@ uschar **); extern int route_address(address_item *, address_item **, address_item **, address_item **, address_item **, int); -extern int route_check_prefix(uschar *, uschar *); -extern int route_check_suffix(uschar *, uschar *); +extern int route_check_prefix(const uschar *, const uschar *); +extern int route_check_suffix(const uschar *, const uschar *); extern BOOL route_findgroup(uschar *, gid_t *); -extern BOOL route_finduser(uschar *, struct passwd **, uid_t *); +extern BOOL route_finduser(const uschar *, struct passwd **, uid_t *); extern BOOL route_find_expanded_group(uschar *, uschar *, uschar *, gid_t *, uschar **); extern BOOL route_find_expanded_user(uschar *, uschar *, uschar *, @@ -321,10 +351,10 @@ extern void route_init(void); extern void route_tidyup(void); -extern uschar *search_find(void *, uschar *, uschar *, int, uschar *, int, +extern uschar *search_find(void *, uschar *, uschar *, int, const uschar *, int, int, int *); -extern int search_findtype(uschar *, int); -extern int search_findtype_partial(uschar *, int *, uschar **, int *, +extern int search_findtype(const uschar *, int); +extern int search_findtype_partial(const uschar *, int *, const uschar **, int *, int *); extern void *search_open(uschar *, int, int, uid_t *, gid_t *); extern void search_tidyup(void); @@ -337,7 +367,10 @@ extern void sigalrm_handler(int); extern BOOL smtp_buffered(void); extern void smtp_closedown(uschar *); -extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL, const uschar *); +extern int smtp_connect(host_item *, int, int, uschar *, int, + transport_instance *); +extern int smtp_sock_connect(host_item *, int, int, uschar *, + transport_instance * tb, int); extern int smtp_feof(void); extern int smtp_ferror(void); extern uschar *smtp_get_connection_info(void); @@ -358,8 +391,8 @@ extern BOOL smtp_verify_helo(void); extern int smtp_write_command(smtp_outblock *, BOOL, const char *, ...) PRINTF_FUNCTION(3,4); #ifdef WITH_CONTENT_SCAN -extern int spam(uschar **); -extern FILE *spool_mbox(unsigned long *, uschar *); +extern int spam(const uschar **); +extern FILE *spool_mbox(unsigned long *, const uschar *); #endif extern BOOL spool_move_message(uschar *, uschar *, uschar *, uschar *); extern BOOL spool_open_datafile(uschar *); @@ -372,38 +405,50 @@ extern int stdin_ungetc(int); extern uschar *string_append(uschar *, int *, int *, int, ...); extern uschar *string_append_listele(uschar *, uschar, const uschar *); +extern uschar *string_append_listele_n(uschar *, uschar, const uschar *, unsigned); extern uschar *string_base62(unsigned long int); extern uschar *string_cat(uschar *, int *, int *, const uschar *, int); extern uschar *string_copy_dnsdomain(uschar *); -extern uschar *string_copy_malloc(uschar *); -extern uschar *string_copylc(uschar *); +extern uschar *string_copy_malloc(const uschar *); +extern uschar *string_copylc(const uschar *); extern uschar *string_copynlc(uschar *, int); -extern uschar *string_dequote(uschar **); +extern uschar *string_dequote(const uschar **); extern BOOL string_format(uschar *, int, const char *, ...) ALMOST_PRINTF(3,4); extern uschar *string_format_size(int, uschar *); -extern int string_interpret_escape(uschar **); +extern int string_interpret_escape(const uschar **); extern int string_is_ip_address(const uschar *, int *); +#ifdef EXPERIMENTAL_INTERNATIONAL +extern BOOL string_is_utf8(const uschar *); +#endif extern uschar *string_log_address(address_item *, BOOL, BOOL); -extern uschar *string_nextinlist(uschar **, int *, uschar *, int); +extern uschar *string_nextinlist(const uschar **, int *, uschar *, int); extern uschar *string_open_failed(int, const char *, ...) PRINTF_FUNCTION(2,3); -extern uschar *string_printing2(uschar *, BOOL); +extern const uschar *string_printing2(const uschar *, BOOL); extern uschar *string_split_message(uschar *); extern uschar *string_unprinting(uschar *); +#ifdef EXPERIMENTAL_INTERNATIONAL +extern uschar *string_address_utf8_to_alabel(const uschar *, uschar **); +extern uschar *string_domain_alabel_to_utf8(const uschar *, uschar **); +extern uschar *string_domain_utf8_to_alabel(const uschar *, uschar **); +extern uschar *string_localpart_alabel_to_utf8(const uschar *, uschar **); +extern uschar *string_localpart_utf8_to_alabel(const uschar *, uschar **); +#endif extern BOOL string_vformat(uschar *, int, const char *, va_list); extern int strcmpic(const uschar *, const uschar *); extern int strncmpic(const uschar *, const uschar *, int); extern uschar *strstric(uschar *, uschar *, BOOL); extern uschar *tod_stamp(int); + extern void tls_modify_variables(tls_support *); -extern BOOL transport_check_waiting(uschar *, uschar *, int, uschar *, - BOOL *); +extern BOOL transport_check_waiting(const uschar *, const uschar *, int, uschar *, + BOOL *, oicf, void*); extern void transport_init(void); -extern BOOL transport_pass_socket(uschar *, uschar *, uschar *, uschar *, +extern BOOL transport_pass_socket(const uschar *, const uschar *, const uschar *, uschar *, int); extern uschar *transport_rcpt_address(address_item *, BOOL); -extern BOOL transport_set_up_command(uschar ***, uschar *, BOOL, int, - address_item *, uschar *, uschar **); +extern BOOL transport_set_up_command(const uschar ***, uschar *, + BOOL, int, address_item *, uschar *, uschar **); extern void transport_update_waiting(host_item *, uschar *); extern BOOL transport_write_block(int, uschar *, int); extern BOOL transport_write_string(int, const char *, ...); @@ -415,25 +460,29 @@ extern void tree_add_nonrecipient(uschar *); extern void tree_add_unusable(host_item *); extern int tree_insertnode(tree_node **, tree_node *); -extern tree_node *tree_search(tree_node *, uschar *); +extern tree_node *tree_search(tree_node *, const uschar *); extern void tree_write(tree_node *, FILE *); extern void tree_walk(tree_node *, void (*)(uschar*, uschar*, void*), void *); #ifdef WITH_CONTENT_SCAN extern void unspool_mbox(void); #endif +#ifdef EXPERIMENTAL_INTERNATIONAL +extern void utf8_version_report(FILE *); +#endif extern int verify_address(address_item *, FILE *, int, int, int, int, uschar *, uschar *, BOOL *); -extern int verify_check_dnsbl(uschar **); +extern int verify_check_dnsbl(const uschar **); extern int verify_check_header_address(uschar **, uschar **, int, int, int, uschar *, uschar *, int, int *); extern int verify_check_headers(uschar **); extern int verify_check_header_names_ascii(uschar **); extern int verify_check_host(uschar **); extern int verify_check_notblind(void); -extern int verify_check_this_host(uschar **, unsigned int *, uschar*, - uschar *, uschar **); +extern int verify_check_given_host(uschar **, host_item *); +extern int verify_check_this_host(const uschar **, unsigned int *, + const uschar*, const uschar *, const uschar **); extern address_item *verify_checked_sender(uschar *); extern void verify_get_ident(int); extern BOOL verify_sender(int *, uschar **); diff -Nru exim4-4.84/src/globals.c exim4-4.86~RC4/src/globals.c --- exim4-4.84/src/globals.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/globals.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* All the global variables are defined together in this one module, so @@ -103,6 +103,10 @@ -1, /* tls_active */ 0, /* tls_bits */ FALSE,/* tls_certificate_verified */ +#ifdef EXPERIMENTAL_DANE + FALSE,/* dane_verified */ + 0, /* tlsa_usage */ +#endif NULL, /* tls_cipher */ FALSE,/* tls_on_connect */ NULL, /* tls_on_connect_ports */ @@ -116,6 +120,10 @@ -1, /* tls_active */ 0, /* tls_bits */ FALSE,/* tls_certificate_verified */ +#ifdef EXPERIMENTAL_DANE + FALSE,/* dane_verified */ + 0, /* tlsa_usage */ +#endif NULL, /* tls_cipher */ FALSE,/* tls_on_connect */ NULL, /* tls_on_connect_ports */ @@ -126,13 +134,11 @@ 0 /* tls_ocsp */ }; -#ifdef EXPERIMENTAL_DSN uschar *dsn_envid = NULL; int dsn_ret = 0; const pcre *regex_DSN = NULL; BOOL smtp_use_dsn = FALSE; uschar *dsn_advertise_hosts = NULL; -#endif #ifdef SUPPORT_TLS BOOL gnutls_compat_mode = FALSE; @@ -150,6 +156,7 @@ bit-count as "NORMAL" (2432) and Thunderbird dropping connection. */ int tls_dh_max_bits = 2236; uschar *tls_dhparam = NULL; +uschar *tls_eccurve = US"prime256v1"; #ifndef DISABLE_OCSP uschar *tls_ocsp_file = NULL; #endif @@ -158,7 +165,7 @@ BOOL tls_remember_esmtp = FALSE; uschar *tls_require_ciphers = NULL; uschar *tls_try_verify_hosts = NULL; -uschar *tls_verify_certificates= NULL; +uschar *tls_verify_certificates= US"system"; uschar *tls_verify_hosts = NULL; #endif @@ -169,6 +176,10 @@ const pcre *regex_PRDR = NULL; #endif +#ifdef EXPERIMENTAL_INTERNATIONAL +const pcre *regex_UTF8 = NULL; +#endif + /* Input-reading functions for messages, so we can use special ones for incoming TCP/IP. The defaults use stdin. We never need these for any stand-alone tests. */ @@ -186,24 +197,24 @@ when verifying one address while routing/verifying another. We have to have the size explicit, because it is referenced from more than one module. */ -uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT] = { - &deliver_address_data, - &deliver_domain, - &deliver_domain_data, - &deliver_domain_orig, - &deliver_domain_parent, - &deliver_localpart, - &deliver_localpart_data, - &deliver_localpart_orig, - &deliver_localpart_parent, - &deliver_localpart_prefix, - &deliver_localpart_suffix, - (uschar **)(&deliver_recipients), - &deliver_host, - &deliver_home, - &address_file, - &address_pipe, - &self_hostname, +const uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT] = { + CUSS &deliver_address_data, + CUSS &deliver_domain, + CUSS &deliver_domain_data, + CUSS &deliver_domain_orig, + CUSS &deliver_domain_parent, + CUSS &deliver_localpart, + CUSS &deliver_localpart_data, + CUSS &deliver_localpart_orig, + CUSS &deliver_localpart_parent, + CUSS &deliver_localpart_prefix, + CUSS &deliver_localpart_suffix, + CUSS (uschar **)(&deliver_recipients), + CUSS &deliver_host, + CUSS &deliver_home, + CUSS &address_file, + CUSS &address_pipe, + CUSS &self_hostname, NULL }; int address_expansions_count = sizeof(address_expansions)/sizeof(uschar **); @@ -226,7 +237,7 @@ uschar *acl_smtp_connect = NULL; uschar *acl_smtp_data = NULL; #ifndef DISABLE_PRDR -uschar *acl_smtp_data_prdr = NULL; +uschar *acl_smtp_data_prdr = US"accept"; #endif #ifndef DISABLE_DKIM uschar *acl_smtp_dkim = NULL; @@ -353,11 +364,9 @@ NULL, /* authenticator */ NULL, /* auth_id */ NULL, /* auth_sndr */ - #ifdef EXPERIMENTAL_DSN NULL, /* dsn_orcpt */ 0, /* dsn_flags */ 0, /* dsn_aware */ - #endif (uid_t)(-1), /* uid */ (gid_t)(-1), /* gid */ 0, /* flags */ @@ -380,6 +389,9 @@ #ifdef EXPERIMENTAL_SRS NULL, /* srs_sender */ #endif +#ifdef EXPERIMENTAL_INTERNATIONAL + FALSE, /* utf8 */ +#endif } }; @@ -488,6 +500,7 @@ uschar *config_main_filelist = US CONFIGURE_FILE "\0<-----------Space to patch configure_filename->"; uschar *config_main_filename = NULL; +uschar *config_main_directory = NULL; #ifdef CONFIGURE_OWNER uid_t config_uid = CONFIGURE_OWNER; @@ -501,8 +514,11 @@ uschar *continue_transport = NULL; uschar *csa_status = NULL; -BOOL cutthrough_delivery = FALSE; -int cutthrough_fd = -1; +cut_t cutthrough = { + FALSE, /* delivery: when to attempt */ + -1, /* fd: open connection */ + 0, /* nrcpt: number of addresses */ +}; BOOL daemon_listen = FALSE; uschar *daemon_smtp_port = US"smtp"; @@ -565,18 +581,19 @@ BOOL delivery_date_remove = TRUE; uschar *deliver_address_data = NULL; int deliver_datafile = -1; -uschar *deliver_domain = NULL; +const uschar *deliver_domain = NULL; uschar *deliver_domain_data = NULL; -uschar *deliver_domain_orig = NULL; -uschar *deliver_domain_parent = NULL; +const uschar *deliver_domain_orig = NULL; +const uschar *deliver_domain_parent = NULL; BOOL deliver_drop_privilege = FALSE; BOOL deliver_firsttime = FALSE; BOOL deliver_force = FALSE; BOOL deliver_freeze = FALSE; time_t deliver_frozen_at = 0; uschar *deliver_home = NULL; -uschar *deliver_host = NULL; -uschar *deliver_host_address = NULL; +const uschar *deliver_host = NULL; +const uschar *deliver_host_address = NULL; +int deliver_host_port = 0; uschar *deliver_in_buffer = NULL; ino_t deliver_inode = 0; uschar *deliver_localpart = NULL; @@ -633,10 +650,14 @@ uschar *dns_again_means_nonexist = NULL; int dns_csa_search_limit = 5; BOOL dns_csa_use_reverse = TRUE; +#ifdef EXPERIMENTAL_DANE +int dns_dane_ok = -1; +#endif uschar *dns_ipv4_lookup = NULL; int dns_retrans = 0; int dns_retry = 0; int dns_dnssec_ok = -1; /* <0 = not coerced */ +uschar *dns_trust_aa = NULL; int dns_use_edns0 = -1; /* <0 = not coerced */ uschar *dnslist_domain = NULL; uschar *dnslist_matched = NULL; @@ -656,6 +677,13 @@ int error_handling = ERRORS_SENDER; uschar *errors_reply_to = NULL; int errors_sender_rc = EXIT_FAILURE; +#ifdef EXPERIMENTAL_EVENT +uschar *event_action = NULL; /* expansion for delivery events */ +uschar *event_data = NULL; /* auxilary data variable for event */ +int event_defer_errno = 0; +const uschar *event_name = NULL; /* event name variable */ +#endif + gid_t exim_gid = EXIM_GID; BOOL exim_gid_set = TRUE; /* This gid is always set */ @@ -887,6 +915,10 @@ BOOL message_logs = TRUE; int message_size = 0; uschar *message_size_limit = US"50M"; +#ifdef EXPERIMENTAL_INTERNATIONAL +BOOL message_smtputf8 = FALSE; +int message_utf8_downconvert = 0; /* -1 ifneeded; 0 never; 1 always */ +#endif uschar message_subdir[2] = { 0, 0 }; uschar *message_reference = NULL; @@ -956,7 +988,7 @@ uschar *prvscheck_result = NULL; -uschar *qualify_domain_recipient = NULL; +const uschar *qualify_domain_recipient = NULL; uschar *qualify_domain_sender = NULL; BOOL queue_2stage = FALSE; uschar *queue_domains = NULL; @@ -1056,8 +1088,8 @@ uschar *return_path = NULL; BOOL return_path_remove = TRUE; int rewrite_existflags = 0; -uschar *rfc1413_hosts = US"*"; -int rfc1413_query_timeout = 5; +uschar *rfc1413_hosts = US"@[]"; +int rfc1413_query_timeout = 0; /* BOOL rfc821_domains = FALSE; <<< on the way out */ uid_t root_gid = ROOT_GID; uid_t root_uid = ROOT_UID; @@ -1129,9 +1161,7 @@ TRUE, /* verify_sender */ FALSE, /* uid_set */ FALSE, /* unseen */ -#ifdef EXPERIMENTAL_DSN FALSE, /* dsn_lasthop */ -#endif self_freeze, /* self_code */ (uid_t)(-1), /* uid */ @@ -1140,7 +1170,9 @@ NULL, /* fallback_hostlist */ NULL, /* transport instance */ NULL, /* pass_router */ - NULL /* redirect_router */ + NULL, /* redirect_router */ + + { NULL, NULL }, /* dnssec_domains {require,request} */ }; uschar *router_name = NULL; @@ -1171,6 +1203,7 @@ uschar *sender_data = NULL; unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; uschar *sender_fullhost = NULL; +BOOL sender_helo_dnssec = FALSE; uschar *sender_helo_name = NULL; uschar **sender_host_aliases = &no_aliases; uschar *sender_host_address = NULL; @@ -1199,6 +1232,7 @@ int sending_port = -1; SIGNAL_BOOL sigalrm_seen = FALSE; uschar **sighup_argv = NULL; +int slow_lookup_log = 0; /* millisecs, zero disables */ int smtp_accept_count = 0; BOOL smtp_accept_keepalive = TRUE; int smtp_accept_max = 20; @@ -1240,6 +1274,7 @@ uschar *smtp_ratelimit_rcpt = NULL; uschar *smtp_read_error = US""; int smtp_receive_timeout = 5*60; +uschar *smtp_receive_timeout_s = NULL; uschar *smtp_reserve_hosts = NULL; BOOL smtp_return_error_details = FALSE; int smtp_rlm_base = 0; @@ -1252,11 +1287,15 @@ int smtp_rlr_threshold = INT_MAX; BOOL smtp_use_pipelining = FALSE; BOOL smtp_use_size = FALSE; +#ifdef EXPERIMENTAL_INTERNATIONAL +uschar *smtputf8_advertise_hosts = US"*"; /* overridden under test-harness */ +#endif #ifdef WITH_CONTENT_SCAN uschar *spamd_address = US"127.0.0.1 783"; uschar *spam_bar = NULL; uschar *spam_report = NULL; +uschar *spam_action = NULL; uschar *spam_score = NULL; uschar *spam_score_int = NULL; #endif @@ -1324,17 +1363,6 @@ int timeout_frozen_after = 0; BOOL timestamps_utc = FALSE; -#ifdef EXPERIMENTAL_TPDA -int tpda_defer_errno = 0; -uschar *tpda_defer_errstr = NULL; -uschar *tpda_delivery_ip = NULL; -int tpda_delivery_port = 0; -uschar *tpda_delivery_fqdn = NULL; -uschar *tpda_delivery_local_part= NULL; -uschar *tpda_delivery_domain = NULL; -uschar *tpda_delivery_confirmation = NULL; -#endif - transport_instance *transports = NULL; transport_instance transport_defaults = { @@ -1348,6 +1376,7 @@ NULL, /* batch_id */ NULL, /* home_dir */ NULL, /* current_dir */ + NULL, /* expand-multi-domain */ TRUE, /* multi-domain */ FALSE, /* overrides_hosts */ 100, /* max_addresses */ @@ -1387,15 +1416,15 @@ FALSE, /* log_defer_output */ TRUE_UNSET /* retry_use_local_part: BOOL, but set neither 1 nor 0 so can detect unset */ -#ifdef EXPERIMENTAL_TPDA - ,NULL /* tpda_delivery_action */ +#ifdef EXPERIMENTAL_EVENT + ,NULL /* event_action */ #endif }; int transport_count; uschar *transport_name = NULL; int transport_newlines; -uschar **transport_filter_argv = NULL; +const uschar **transport_filter_argv = NULL; int transport_filter_timeout; BOOL transport_filter_timed_out = FALSE; int transport_write_timeout= 0; @@ -1440,18 +1469,19 @@ uschar *uucp_from_sender = US"$1"; -uschar *warn_message_file = NULL; -uschar *warnmsg_delay = NULL; -uschar *warnmsg_recipients = NULL; -BOOL write_rejectlog = TRUE; - +uschar *verify_mode = NULL; uschar *version_copyright = - US"Copyright (c) University of Cambridge, 1995 - 2014\n" - "(c) The Exim Maintainers and contributors in ACKNOWLEDGMENTS file, 2007 - 2014"; + US"Copyright (c) University of Cambridge, 1995 - 2015\n" + "(c) The Exim Maintainers and contributors in ACKNOWLEDGMENTS file, 2007 - 2015"; uschar *version_date = US"?"; uschar *version_cnumber = US"????"; uschar *version_string = US"?"; +uschar *warn_message_file = NULL; int warning_count = 0; +uschar *warnmsg_delay = NULL; +uschar *warnmsg_recipients = NULL; +BOOL write_rejectlog = TRUE; + /* End of globals.c */ diff -Nru exim4-4.84/src/globals.h exim4-4.86~RC4/src/globals.h --- exim4-4.84/src/globals.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/globals.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Almost all the global variables are defined together in this one header, so @@ -82,6 +82,10 @@ int active; /* fd/socket when in a TLS session */ int bits; /* bits used in TLS session */ BOOL certificate_verified; /* Client certificate verified */ +#ifdef EXPERIMENTAL_DANE + BOOL dane_verified; /* ... via DANE */ + int tlsa_usage; /* TLSA record(s) usage */ +#endif uschar *cipher; /* Cipher used */ BOOL on_connect; /* For older MTAs that don't STARTTLS */ uschar *on_connect_ports; /* Ports always tls-on-connect */ @@ -114,6 +118,7 @@ extern uschar *tls_crl; /* CRL File */ extern int tls_dh_max_bits; /* don't accept higher lib suggestions */ extern uschar *tls_dhparam; /* DH param file */ +extern uschar *tls_eccurve; /* EC curve */ #ifndef DISABLE_OCSP extern uschar *tls_ocsp_file; /* OCSP stapling proof file */ #endif @@ -126,13 +131,11 @@ extern uschar *tls_verify_hosts; /* Mandatory client verification */ #endif -#ifdef EXPERIMENTAL_DSN extern uschar *dsn_envid; /* DSN envid string */ extern int dsn_ret; /* DSN ret type*/ extern const pcre *regex_DSN; /* For recognizing DSN settings */ extern BOOL smtp_use_dsn; /* Global for passed connections */ extern uschar *dsn_advertise_hosts; /* host for which TLS is advertised */ -#endif /* Input-reading functions for messages, so we can use special ones for incoming TCP/IP. */ @@ -148,7 +151,7 @@ the size of this vector set explicitly, because it is referenced from more than one module. */ -extern uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT]; +extern const uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT]; /* General global variables */ @@ -278,6 +281,7 @@ extern int config_lineno; /* Line number */ extern uschar *config_main_filelist; /* List of possible config files */ extern uschar *config_main_filename; /* File name actually used */ +extern uschar *config_main_directory; /* Directory where the main config file was found */ #ifdef CONFIGURE_OWNER extern uid_t config_uid; /* Additional owner */ #endif @@ -288,8 +292,16 @@ extern uschar *continue_transport; /* Transport for continued delivery */ extern uschar *csa_status; /* Client SMTP Authorization result */ -extern BOOL cutthrough_delivery; /* Deliver in foreground */ -extern int cutthrough_fd; /* Connection for ditto */ + +typedef struct { + BOOL delivery; /* When to attempt */ + int fd; /* Open connection */ + int nrcpt; /* Count of addresses */ + uschar * interface; /* (address of) */ + host_item host; /* Host used */ + address_item addr; /* (Chain of) addresses */ +} cut_t; +extern cut_t cutthrough; /* Deliver-concurrently */ extern BOOL daemon_listen; /* True if listening required */ extern uschar *daemon_smtp_port; /* Can be a list of ports */ @@ -315,19 +327,20 @@ extern uschar *deliver_address_data; /* Arbitrary data for an address */ extern int deliver_datafile; /* FD for data part of message */ -extern uschar *deliver_domain; /* The local domain for delivery */ +extern const uschar *deliver_domain; /* The local domain for delivery */ extern uschar *deliver_domain_data; /* From domain lookup */ -extern uschar *deliver_domain_orig; /* The original local domain for delivery */ -extern uschar *deliver_domain_parent; /* The parent domain for delivery */ +extern const uschar *deliver_domain_orig; /* The original local domain for delivery */ +extern const uschar *deliver_domain_parent; /* The parent domain for delivery */ extern BOOL deliver_drop_privilege; /* TRUE for unprivileged delivery */ extern BOOL deliver_firsttime; /* True for first delivery attempt */ extern BOOL deliver_force; /* TRUE if delivery was forced */ extern BOOL deliver_freeze; /* TRUE if delivery is frozen */ extern time_t deliver_frozen_at; /* Time of freezing */ extern uschar *deliver_home; /* Home directory for pipes */ -extern uschar *deliver_host; /* (First) host for routed local deliveries */ +extern const uschar *deliver_host; /* (First) host for routed local deliveries */ /* Remote host for filter */ -extern uschar *deliver_host_address; /* Address for remote delivery filter */ +extern const uschar *deliver_host_address; /* Address for remote delivery filter */ +extern int deliver_host_port; /* Address for remote delivery filter */ extern uschar *deliver_in_buffer; /* Buffer for copying file */ extern ino_t deliver_inode; /* Inode for appendfile */ extern uschar *deliver_localpart; /* The local part for delivery */ @@ -385,9 +398,13 @@ extern int dns_csa_search_limit; /* How deep to search for CSA SRV records */ extern BOOL dns_csa_use_reverse; /* Check CSA in reverse DNS? (non-standard) */ extern uschar *dns_ipv4_lookup; /* For these domains, don't look for AAAA (or A6) */ +#ifdef EXPERIMENTAL_DANE +extern int dns_dane_ok; /* Ok to use DANE when checking TLS authenticity */ +#endif extern int dns_retrans; /* Retransmission time setting */ extern int dns_retry; /* Number of retries */ extern int dns_dnssec_ok; /* When constructing DNS query, set DO flag */ +extern uschar *dns_trust_aa; /* DNSSEC trust AA as AD */ extern int dns_use_edns0; /* Coerce EDNS0 support on/off in resolver. */ extern uschar *dnslist_domain; /* DNS (black) list domain */ extern uschar *dnslist_matched; /* DNS (black) list matched key */ @@ -410,6 +427,14 @@ extern uschar *errors_copy; /* For taking copies of errors */ extern uschar *errors_reply_to; /* Reply-to for error messages */ extern int errors_sender_rc; /* Return after message to sender*/ + +#ifdef EXPERIMENTAL_EVENT +extern uschar *event_action; /* expansion for delivery events */ +extern uschar *event_data; /* event data */ +extern int event_defer_errno; /* error number set when a remote delivery is deferred with a host error */ +extern const uschar *event_name; /* event classification */ +#endif + extern gid_t exim_gid; /* To be used with exim_uid */ extern BOOL exim_gid_set; /* TRUE if exim_gid set */ extern uschar *exim_path; /* Path to exec exim */ @@ -547,6 +572,11 @@ extern BOOL message_logs; /* TRUE to write message logs */ extern int message_size; /* Size of message */ extern uschar *message_size_limit; /* As it says */ +#ifdef EXPERIMENTAL_INTERNATIONAL +extern BOOL message_smtputf8; /* Internationalized mail handling */ +extern int message_utf8_downconvert; /* convert from utf8 */ +const extern pcre *regex_UTF8; /* For recognizing SMTPUTF8 settings */ +#endif extern uschar message_subdir[]; /* Subdirectory for messages */ extern uschar *message_reference; /* Reference for error messages */ @@ -625,7 +655,7 @@ extern uschar *prvscheck_keynum; /* Set during prvscheck expansion item */ extern uschar *prvscheck_result; /* Set during prvscheck expansion item */ -extern uschar *qualify_domain_recipient; /* Domain to qualify recipients with */ +extern const uschar *qualify_domain_recipient; /* Domain to qualify recipients with */ extern uschar *qualify_domain_sender; /* Domain to qualify senders with */ extern BOOL queue_2stage; /* Run queue in 2-stage manner */ extern uschar *queue_domains; /* Queue these domains */ @@ -727,6 +757,7 @@ extern uschar *sender_data; /* lookup result for senders */ extern unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for sender domain */ extern uschar *sender_fullhost; /* Sender host name + address */ +extern BOOL sender_helo_dnssec; /* True if HELO verify used DNS and was DNSSEC */ extern uschar *sender_helo_name; /* Host name from HELO/EHLO */ extern uschar **sender_host_aliases; /* Points to list of alias names */ extern unsigned int sender_host_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for incoming host */ @@ -749,6 +780,7 @@ extern int sending_port; /* Port of outgoing interface */ extern SIGNAL_BOOL sigalrm_seen; /* Flag for sigalrm_handler */ extern uschar **sighup_argv; /* Args for re-execing after SIGHUP */ +extern int slow_lookup_log; /* Log DNS lookups taking loger than N millisecs */ extern int smtp_accept_count; /* Count of connections */ extern BOOL smtp_accept_keepalive; /* Set keepalive on incoming */ extern int smtp_accept_max; /* Max SMTP connections */ @@ -786,6 +818,7 @@ extern uschar *smtp_ratelimit_rcpt; /* Parameters for RCPT limiting */ extern uschar *smtp_read_error; /* Message for SMTP input error */ extern int smtp_receive_timeout; /* Applies to each received line */ +extern uschar *smtp_receive_timeout_s; /* ... expandable version */ extern uschar *smtp_reserve_hosts; /* Hosts for reserved slots */ extern BOOL smtp_return_error_details; /* TRUE to return full info */ extern int smtp_rlm_base; /* Base interval for MAIL rate limit */ @@ -798,11 +831,15 @@ extern int smtp_rlr_threshold; /* Threshold for RCPT rate limit */ extern BOOL smtp_use_pipelining; /* Global for passed connections */ extern BOOL smtp_use_size; /* Global for passed connections */ +#ifdef EXPERIMENTAL_INTERNATIONAL +extern uschar *smtputf8_advertise_hosts; /* ingress control */ +#endif #ifdef WITH_CONTENT_SCAN extern uschar *spamd_address; /* address for the spamassassin daemon */ extern uschar *spam_bar; /* the spam "bar" (textual representation of spam_score) */ extern uschar *spam_report; /* the spamd report (multiline) */ +extern uschar *spam_action; /* the spamd recommended-action */ extern uschar *spam_score; /* the spam score (float) */ extern uschar *spam_score_int; /* spam_score * 10 (int) */ #endif @@ -868,21 +905,10 @@ extern int timeout_frozen_after; /* Max time to keep frozen messages */ extern BOOL timestamps_utc; /* Use UTC for all times */ -#ifdef EXPERIMENTAL_TPDA -extern int tpda_defer_errno; /* error number set when a remote delivery is deferred with a host error */ -extern uschar *tpda_defer_errstr; /* error string set when a remote delivery is deferred with a host error */ -extern uschar *tpda_delivery_ip; /* IP of host, which has accepted delivery */ -extern int tpda_delivery_port; /* port of host, which has accepted delivery */ -extern uschar *tpda_delivery_fqdn; /* FQDN of host, which has accepted delivery */ -extern uschar *tpda_delivery_local_part;/* local part of address being delivered */ -extern uschar *tpda_delivery_domain; /* domain part of address being delivered */ -extern uschar *tpda_delivery_confirmation; /* SMTP confirmation message */ -#endif - extern uschar *transport_name; /* Name of transport last started */ extern int transport_count; /* Count of bytes transported */ extern int transport_newlines; /* Accurate count of number of newline chars transported */ -extern uschar **transport_filter_argv; /* For on-the-fly filtering */ +extern const uschar **transport_filter_argv; /* For on-the-fly filtering */ extern int transport_filter_timeout; /* Timeout for same */ extern BOOL transport_filter_timed_out; /* True if it did */ @@ -914,6 +940,7 @@ extern uschar *warnmsg_recipients; /* Recipients of warning message */ extern BOOL write_rejectlog; /* Control of reject logging */ +extern uschar *verify_mode; /* Running a router in verify mode */ extern uschar *version_copyright; /* Copyright notice */ extern uschar *version_date; /* Date of compilation */ extern uschar *version_cnumber; /* Compile number */ diff -Nru exim4-4.84/src/host.c exim4-4.86~RC4/src/host.c --- exim4-4.84/src/host.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/host.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or @@ -94,6 +94,53 @@ return (unsigned int)(random_seed >> 16) % limit; } +/************************************************* +* Wrappers for logging lookup times * +*************************************************/ + +/* When the 'slow_lookup_log' variable is enabled, these wrappers will +write to the log file all (potential) dns lookups that take more than +slow_lookup_log milliseconds +*/ + +static void +log_long_lookup(const uschar * type, const uschar * data, unsigned long msec) +{ +log_write(0, LOG_MAIN, "Long %s lookup for '%s': %lu msec", + type, data, msec); +} + + +/* returns the current system epoch time in milliseconds. */ +static unsigned long +get_time_in_ms() +{ +struct timeval tmp_time; +unsigned long seconds, microseconds; + +gettimeofday(&tmp_time, NULL); +seconds = (unsigned long) tmp_time.tv_sec; +microseconds = (unsigned long) tmp_time.tv_usec; +return seconds*1000 + microseconds/1000; +} + + +static int +dns_lookup_timerwrap(dns_answer *dnsa, const uschar *name, int type, + const uschar **fully_qualified_name) +{ +int retval; +unsigned long time_msec; + +if (!slow_lookup_log) + return dns_lookup(dnsa, name, type, fully_qualified_name); + +time_msec = get_time_in_ms(); +retval = dns_lookup(dnsa, name, type, fully_qualified_name); +if ((time_msec = get_time_in_ms() - time_msec) > slow_lookup_log) + log_long_lookup(US"name", name, time_msec); +return retval; +} /************************************************* @@ -101,8 +148,7 @@ *************************************************/ /* This function is called instead of gethostbyname(), gethostbyname2(), or -getipnodebyname() when running in the test harness. It recognizes the name -"manyhome.test.ex" and generates a humungous number of IP addresses. It also +getipnodebyname() when running in the test harness. . It also recognizes an unqualified "localhost" and forces it to the appropriate loopback address. IP addresses are treated as literals. For other names, it uses the DNS to find the host name. In the test harness, this means it will access only the @@ -118,7 +164,7 @@ */ static struct hostent * -host_fake_gethostbyname(uschar *name, int af, int *error_num) +host_fake_gethostbyname(const uschar *name, int af, int *error_num) { #if HAVE_IPV6 int alen = (af == AF_INET)? sizeof(struct in_addr):sizeof(struct in6_addr); @@ -127,7 +173,7 @@ #endif int ipa; -uschar *lname = name; +const uschar *lname = name; uschar *adds; uschar **alist; struct hostent *yield; @@ -139,34 +185,6 @@ debug_printf("using host_fake_gethostbyname for %s (%s)\n", name, (af == AF_INET)? "IPv4" : "IPv6"); -/* Handle the name that needs a vast number of IP addresses */ - -if (Ustrcmp(name, "manyhome.test.ex") == 0 && af == AF_INET) - { - int i, j; - yield = store_get(sizeof(struct hostent)); - alist = store_get(2049 * sizeof(char *)); - adds = store_get(2048 * alen); - yield->h_name = CS name; - yield->h_aliases = NULL; - yield->h_addrtype = af; - yield->h_length = alen; - yield->h_addr_list = CSS alist; - for (i = 104; i <= 111; i++) - { - for (j = 0; j <= 255; j++) - { - *alist++ = adds; - *adds++ = 10; - *adds++ = 250; - *adds++ = i; - *adds++ = j; - } - } - *alist = NULL; - return yield; - } - /* Handle unqualified "localhost" */ if (Ustrcmp(name, "localhost") == 0) @@ -217,7 +235,7 @@ else { int type = (af == AF_INET)? T_A:T_AAAA; - int rc = dns_lookup(&dnsa, lname, type, NULL); + int rc = dns_lookup_timerwrap(&dnsa, lname, type, NULL); int count = 0; lookup_dnssec_authenticated = NULL; @@ -233,11 +251,10 @@ } for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) - { - if (rr->type == type) count++; - } + if (rr->type == type) + count++; yield = store_get(sizeof(struct hostent)); alist = store_get((count + 1) * sizeof(char **)); @@ -250,14 +267,14 @@ yield->h_addr_list = CSS alist; for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { int i, n; int x[4]; dns_address *da; if (rr->type != type) continue; - da = dns_address_from_rr(&dnsa, rr); + if (!(da = dns_address_from_rr(&dnsa, rr))) break; *alist++ = adds; n = host_aton(da->address, x); for (i = 0; i < n; i++) @@ -295,19 +312,18 @@ */ void -host_build_hostlist(host_item **anchor, uschar *list, BOOL randomize) +host_build_hostlist(host_item **anchor, const uschar *list, BOOL randomize) { int sep = 0; int fake_mx = MX_NONE; /* This value is actually -1 */ uschar *name; -uschar buffer[1024]; if (list == NULL) return; if (randomize) fake_mx--; /* Start at -2 for randomizing */ *anchor = NULL; -while ((name = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) +while ((name = string_nextinlist(&list, &sep, NULL, 0)) != NULL) { host_item *h; @@ -318,7 +334,7 @@ } h = store_get(sizeof(host_item)); - h->name = string_copy(name); + h->name = name; h->address = NULL; h->port = PORT_NONE; h->mx = fake_mx; @@ -444,7 +460,7 @@ int host_item_get_port(host_item *h) { -uschar *p; +const uschar *p; int port, x; int len = Ustrlen(h->name); @@ -718,7 +734,7 @@ */ ip_address_item * -host_build_ifacelist(uschar *list, uschar *name) +host_build_ifacelist(const uschar *list, uschar *name) { int sep = 0; uschar *s; @@ -811,9 +827,9 @@ if (local_interface_data == NULL) { void *reset_item = store_get(0); - ip_address_item *dlist = host_build_ifacelist(local_interfaces, + ip_address_item *dlist = host_build_ifacelist(CUS local_interfaces, US"local_interfaces"); - ip_address_item *xlist = host_build_ifacelist(extra_local_interfaces, + ip_address_item *xlist = host_build_ifacelist(CUS extra_local_interfaces, US"extra_local_interfaces"); ip_address_item *ipa; @@ -972,7 +988,7 @@ */ int -host_aton(uschar *address, int *bin) +host_aton(const uschar *address, int *bin) { int x[4]; int v4offset = 0; @@ -984,8 +1000,8 @@ if (Ustrchr(address, ':') != NULL) { - uschar *p = address; - uschar *component[8]; + const uschar *p = address; + const uschar *component[8]; BOOL ipv4_ends = FALSE; int ci = 0; int nulloffset = 0; @@ -1179,7 +1195,7 @@ { int sep = 0; uschar buffer[32]; -uschar *list = tls_in.on_connect_ports; +const uschar *list = tls_in.on_connect_ports; uschar *s; uschar *end; @@ -1214,7 +1230,7 @@ */ BOOL -host_is_in_net(uschar *host, uschar *net, int maskoffset) +host_is_in_net(const uschar *host, const uschar *net, int maskoffset) { int i; int address[4]; @@ -1329,9 +1345,9 @@ if (hosts_treat_as_local != NULL) { int rc; - uschar *save = deliver_domain; + const uschar *save = deliver_domain; deliver_domain = h->name; /* set $domain */ - rc = match_isinlist(string_copylc(h->name), &hosts_treat_as_local, 0, + rc = match_isinlist(string_copylc(h->name), CUSS &hosts_treat_as_local, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save; if (rc == OK) goto FOUND_LOCAL; @@ -1455,6 +1471,9 @@ uschar *s, *t; struct hostent *hosts; struct in_addr addr; +unsigned long time_msec; + +if (slow_lookup_log) time_msec = get_time_in_ms(); /* Lookup on IPv6 system */ @@ -1490,6 +1509,11 @@ hosts = gethostbyaddr(CS(&addr), sizeof(addr), AF_INET); #endif +if ( slow_lookup_log + && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log + ) + log_long_lookup(US"name", sender_host_address, time_msec); + /* Failed to look up the host. */ if (hosts == NULL) @@ -1590,7 +1614,7 @@ uschar **aliases; uschar buffer[256]; uschar *ordername; -uschar *list = host_lookup_order; +const uschar *list = host_lookup_order; dns_record *rr; dns_answer dnsa; dns_scan dnss; @@ -1615,14 +1639,13 @@ /* Do lookups directly in the DNS or via gethostbyaddr() (or equivalent), in the order specified by the host_lookup_order option. */ -while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) - != NULL) +while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) { if (strcmpic(ordername, US"bydns") == 0) { dns_init(FALSE, FALSE, FALSE); /* dnssec ctrl by dns_dnssec_ok glbl */ dns_build_reverse(sender_host_address, buffer); - rc = dns_lookup(&dnsa, buffer, T_PTR, NULL); + rc = dns_lookup_timerwrap(&dnsa, buffer, T_PTR, NULL); /* The first record we come across is used for the name; others are considered to be aliases. We have to scan twice, in order to find out the @@ -1637,8 +1660,6 @@ int count = 0; int old_pool = store_pool; - /* Ideally we'd check DNSSEC both forward and reverse, but we use the - gethost* routines for forward, so can't do that unless/until we rewrite. */ sender_host_dnssec = dns_is_secure(&dnsa); DEBUG(D_dns) debug_printf("Reverse DNS security status: %s\n", @@ -1647,11 +1668,10 @@ store_pool = POOL_PERM; /* Save names in permanent storage */ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) - { - if (rr->type == T_PTR) count++; - } + if (rr->type == T_PTR) + count++; /* Get store for the list of aliases. For compatibility with gethostbyaddr, we make an empty list if there are none. */ @@ -1661,7 +1681,7 @@ /* Re-scan and extract the names */ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { uschar *s = NULL; @@ -1686,8 +1706,8 @@ "empty name: treated as non-existent host name\n"); continue; } - if (sender_host_name == NULL) sender_host_name = s; - else *aptr++ = s; + if (!sender_host_name) sender_host_name = s; + else *aptr++ = s; while (*s != 0) { *s = tolower(*s); s++; } } @@ -1742,8 +1762,8 @@ HDEBUG(D_host_lookup) { uschar **aliases = sender_host_aliases; - debug_printf("IP address lookup yielded %s\n", sender_host_name); - while (*aliases != NULL) debug_printf(" alias %s\n", *aliases++); + debug_printf("IP address lookup yielded \"%s\"\n", sender_host_name); + while (*aliases != NULL) debug_printf(" alias \"%s\"\n", *aliases++); } /* We need to verify that a forward lookup on the name we found does indeed @@ -1766,21 +1786,30 @@ int rc; BOOL ok = FALSE; host_item h; + dnssec_domains d; + h.next = NULL; h.name = hname; h.mx = MX_NONE; h.address = NULL; + d.request = sender_host_dnssec ? US"*" : NULL;; + d.require = NULL; - /* When called with the last argument FALSE, host_find_byname() won't return - HOST_FOUND_LOCAL. If the incoming address is an IPv4 address expressed in - IPv6 format, we must compare the IPv4 part to any IPv4 addresses. */ - - if ((rc = host_find_byname(&h, NULL, 0, NULL, FALSE)) == HOST_FOUND) + if ( (rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A, + NULL, NULL, NULL, &d, NULL, NULL)) == HOST_FOUND + || rc == HOST_FOUND_LOCAL + ) { host_item *hh; HDEBUG(D_host_lookup) debug_printf("checking addresses for %s\n", hname); + + /* If the forward lookup was not secure we cancel the is-secure variable */ + + DEBUG(D_dns) debug_printf("Forward DNS security status: %s\n", + h.dnssec == DS_YES ? "DNSSEC verified (AD)" : "unverified"); + if (h.dnssec != DS_YES) sender_host_dnssec = FALSE; + for (hh = &h; hh != NULL; hh = hh->next) - { if (host_is_in_net(hh->address, sender_host_address, 0)) { HDEBUG(D_host_lookup) debug_printf(" %s OK\n", hh->address); @@ -1788,10 +1817,8 @@ break; } else - { HDEBUG(D_host_lookup) debug_printf(" %s\n", hh->address); - } - } + if (!ok) HDEBUG(D_host_lookup) debug_printf("no IP address for %s matched %s\n", hname, sender_host_address); @@ -1804,9 +1831,7 @@ return DEFER; } else - { HDEBUG(D_host_lookup) debug_printf("no IP addresses found for %s\n", hname); - } /* If this name is no good, and it's the sender name, set it null pro tem; if it's an alias, just remove it from the list. */ @@ -1892,8 +1917,8 @@ */ int -host_find_byname(host_item *host, uschar *ignore_target_hosts, int flags, - uschar **fully_qualified_name, BOOL local_host_check) +host_find_byname(host_item *host, const uschar *ignore_target_hosts, int flags, + const uschar **fully_qualified_name, BOOL local_host_check) { int i, yield, times; uschar **addrlist; @@ -1903,22 +1928,12 @@ int af; #endif -/* If we are in the test harness, a name ending in .test.again.dns always -forces a temporary error response, unless the name is in -dns_again_means_nonexist. */ - -if (running_in_test_harness) - { - uschar *endname = host->name + Ustrlen(host->name); - if (Ustrcmp(endname - 14, "test.again.dns") == 0) goto RETURN_AGAIN; - } - /* Make sure DNS options are set as required. This appears to be necessary in some circumstances when the get..byname() function actually calls the DNS. */ dns_init((flags & HOST_FIND_QUALIFY_SINGLE) != 0, (flags & HOST_FIND_SEARCH_PARENTS) != 0, - FALSE); /*XXX dnssec? */ + FALSE); /* Cannot retrieve dnssec status so do not request */ /* In an IPv6 world, unless IPv6 has been disabled, we need to scan for both kinds of address, so go round the loop twice. Note that we have ensured that @@ -1932,8 +1947,8 @@ #else if (disable_ipv6 || (dns_ipv4_lookup != NULL && - match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) == OK)) + match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) == OK)) #endif { af = AF_INET; times = 1; } @@ -1962,11 +1977,14 @@ BOOL ipv4_addr; int error_num = 0; struct hostent *hostdata; + unsigned long time_msec = 0; /* compiler quietening */ #ifdef STAND_ALONE printf("Looking up: %s\n", host->name); #endif + if (slow_lookup_log) time_msec = get_time_in_ms(); + #if HAVE_IPV6 if (running_in_test_harness) hostdata = host_fake_gethostbyname(host->name, af, &error_num); @@ -1990,17 +2008,21 @@ } #endif /* HAVE_IPV6 */ + if ( slow_lookup_log + && (time_msec = get_time_in_ms() - time_msec) > slow_lookup_log) + log_long_lookup(US"name", host->name, time_msec); + if (hostdata == NULL) { uschar *error; switch (error_num) { case HOST_NOT_FOUND: error = US"HOST_NOT_FOUND"; break; - case TRY_AGAIN: error = US"TRY_AGAIN"; break; - case NO_RECOVERY: error = US"NO_RECOVERY"; break; - case NO_DATA: error = US"NO_DATA"; break; + case TRY_AGAIN: error = US"TRY_AGAIN"; break; + case NO_RECOVERY: error = US"NO_RECOVERY"; break; + case NO_DATA: error = US"NO_DATA"; break; #if NO_DATA != NO_ADDRESS - case NO_ADDRESS: error = US"NO_ADDRESS"; break; + case NO_ADDRESS: error = US"NO_ADDRESS"; break; #endif default: error = US"?"; break; } @@ -2116,7 +2138,7 @@ HDEBUG(D_host_lookup) { - host_item *h; + const host_item *h; if (fully_qualified_name != NULL) debug_printf("fully qualified name = %s\n", *fully_qualified_name); debug_printf("%s looked up these IP addresses:\n", @@ -2146,9 +2168,9 @@ { #ifndef STAND_ALONE int rc; - uschar *save = deliver_domain; + const uschar *save = deliver_domain; deliver_domain = host->name; /* set $domain */ - rc = match_isinlist(host->name, &dns_again_means_nonexist, 0, NULL, NULL, + rc = match_isinlist(host->name, CUSS &dns_again_means_nonexist, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save; if (rc == OK) @@ -2196,7 +2218,8 @@ fully_qualified_name if not NULL, return fully qualified name here if the contents are different (i.e. it must be preset to something) - dnnssec_require if TRUE check the DNS result AD bit + dnssec_request if TRUE request the AD bit + dnssec_require if TRUE require the AD bit Returns: HOST_FIND_FAILED couldn't find A record HOST_FIND_AGAIN try again later @@ -2206,8 +2229,9 @@ static int set_address_from_dns(host_item *host, host_item **lastptr, - uschar *ignore_target_hosts, BOOL allow_ip, uschar **fully_qualified_name, - BOOL dnssec_requested, BOOL dnssec_require) + const uschar *ignore_target_hosts, BOOL allow_ip, + const uschar **fully_qualified_name, + BOOL dnssec_request, BOOL dnssec_require) { dns_record *rr; host_item *thishostlast = NULL; /* Indicates not yet filled in anything */ @@ -2231,27 +2255,22 @@ return HOST_FOUND; } -/* On an IPv6 system, unless IPv6 is disabled, go round the loop up to three -times, looking for A6 and AAAA records the first two times. However, unless +/* On an IPv6 system, unless IPv6 is disabled, go round the loop up to twice, +looking for AAAA records the first time. However, unless doing standalone testing, we force an IPv4 lookup if the domain matches -dns_ipv4_lookup is set. Since A6 records look like being abandoned, support -them only if explicitly configured to do so. On an IPv4 system, go round the +dns_ipv4_lookup is set. On an IPv4 system, go round the loop once only, looking only for A records. */ #if HAVE_IPV6 #ifndef STAND_ALONE if (disable_ipv6 || (dns_ipv4_lookup != NULL && - match_isinlist(host->name, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) == OK)) + match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) == OK)) i = 0; /* look up A records only */ else #endif /* STAND_ALONE */ - #ifdef SUPPORT_A6 - i = 2; /* look up A6 and AAAA and A records */ - #else i = 1; /* look up AAAA and A records */ - #endif /* SUPPORT_A6 */ /* The IPv4 world */ @@ -2261,17 +2280,24 @@ for (; i >= 0; i--) { - static int types[] = { T_A, T_AAAA, T_A6 }; + static int types[] = { T_A, T_AAAA }; int type = types[i]; int randoffset = (i == 0)? 500 : 0; /* Ensures v6 sorts before v4 */ dns_answer dnsa; dns_scan dnss; - int rc = dns_lookup(&dnsa, host->name, type, fully_qualified_name); - lookup_dnssec_authenticated = !dnssec_requested ? NULL + int rc = dns_lookup_timerwrap(&dnsa, host->name, type, fully_qualified_name); + lookup_dnssec_authenticated = !dnssec_request ? NULL : dns_is_secure(&dnsa) ? US"yes" : US"no"; - /* We want to return HOST_FIND_AGAIN if one of the A, A6, or AAAA lookups + DEBUG(D_dns) + if ( (dnssec_request || dnssec_require) + && !dns_is_secure(&dnsa) + && dns_is_aa(&dnsa) + ) + debug_printf("DNS lookup of %.256s (A/AAAA) requested AD, but got AA\n", host->name); + + /* We want to return HOST_FIND_AGAIN if one of the A or AAAA lookups fails or times out, but not if another one succeeds. (In the early IPv6 days there are name servers that always fail on AAAA, but are happy to give out an A record. We want to proceed with that A record.) */ @@ -2280,31 +2306,54 @@ { if (i == 0) /* Just tried for an A record, i.e. end of loop */ { - if (host->address != NULL) return HOST_FOUND; /* A6 or AAAA was found */ + if (host->address != NULL) return HOST_FOUND; /* AAAA was found */ if (rc == DNS_AGAIN || rc == DNS_FAIL || v6_find_again) return HOST_FIND_AGAIN; return HOST_FIND_FAILED; /* DNS_NOMATCH or DNS_NODATA */ } - /* Tried for an A6 or AAAA record: remember if this was a temporary + /* Tried for an AAAA record: remember if this was a temporary error, and look for the next record type. */ if (rc != DNS_NOMATCH && rc != DNS_NODATA) v6_find_again = TRUE; continue; } - if (dnssec_require && !dns_is_secure(&dnsa)) + + if (dnssec_request) { - log_write(L_host_lookup_failed, LOG_MAIN, "dnssec fail on %s for %.256s", - i>1 ? "A6" : i>0 ? "AAAA" : "A", host->name); - continue; + if (dns_is_secure(&dnsa)) + { + DEBUG(D_host_lookup) debug_printf("%s A DNSSEC\n", host->name); + if (host->dnssec == DS_UNK) /* set in host_find_bydns() */ + host->dnssec = DS_YES; + } + else + { + if (dnssec_require) + { + log_write(L_host_lookup_failed, LOG_MAIN, + "dnssec fail on %s for %.256s", + i>0 ? "AAAA" : "A", host->name); + continue; + } + if (host->dnssec == DS_YES) /* set in host_find_bydns() */ + { + DEBUG(D_host_lookup) debug_printf("%s A cancel DNSSEC\n", host->name); + host->dnssec = DS_NO; + lookup_dnssec_authenticated = US"no"; + } + } } /* Lookup succeeded: fill in the given host item with the first non-ignored address found; create additional items for any others. A single A6 record - may generate more than one address. */ + may generate more than one address. The lookup had a chance to update the + fqdn; we do not want any later times round the loop to do so. */ + + fully_qualified_name = NULL; for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { if (rr->type == type) @@ -2315,16 +2364,13 @@ da = dns_address_from_rr(&dnsa, rr); DEBUG(D_host_lookup) - { - if (da == NULL) - debug_printf("no addresses extracted from A6 RR for %s\n", + if (!da) debug_printf("no addresses extracted from A6 RR for %s\n", host->name); - } /* This loop runs only once for A and AAAA records, but may run several times for an A6 record that generated multiple addresses. */ - for (; da != NULL; da = da->next) + for (; da; da = da->next) { #ifndef STAND_ALONE if (ignore_target_hosts != NULL && @@ -2411,10 +2457,10 @@ } } -/* Control gets here only if the third lookup (the A record) succeeded. +/* Control gets here only if the econdookup (the A record) succeeded. However, the address may not be filled in if it was ignored. */ -return (host->address == NULL)? HOST_IGNORED : HOST_FOUND; +return host->address ? HOST_FOUND : HOST_IGNORED; } @@ -2444,8 +2490,8 @@ srv_service when SRV used, the service name srv_fail_domains DNS errors for these domains => assume nonexist mx_fail_domains DNS errors for these domains => assume nonexist - dnssec_request_domains => make dnssec request - dnssec_require_domains => ditto and nonexist failures + dnssec_d.request => make dnssec request: domainlist + dnssec_d.require => ditto and nonexist failures fully_qualified_name if not NULL, return fully-qualified name removed set TRUE if local host was removed from the list @@ -2461,10 +2507,10 @@ */ int -host_find_bydns(host_item *host, uschar *ignore_target_hosts, int whichrrs, +host_find_bydns(host_item *host, const uschar *ignore_target_hosts, int whichrrs, uschar *srv_service, uschar *srv_fail_domains, uschar *mx_fail_domains, - uschar *dnssec_request_domains, uschar *dnssec_require_domains, - uschar **fully_qualified_name, BOOL *removed) + const dnssec_domains *dnssec_d, + const uschar **fully_qualified_name, BOOL *removed) { host_item *h, *last; dns_record *rr; @@ -2473,11 +2519,13 @@ int yield; dns_answer dnsa; dns_scan dnss; -BOOL dnssec_require = match_isinlist(host->name, &dnssec_require_domains, +BOOL dnssec_require = dnssec_d + && match_isinlist(host->name, CUSS &dnssec_d->require, 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK; BOOL dnssec_request = dnssec_require - || match_isinlist(host->name, &dnssec_request_domains, - 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK; + || ( dnssec_d + && match_isinlist(host->name, CUSS &dnssec_d->request, + 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK); dnssec_status_t dnssec; /* Set the default fully qualified name to the incoming name, initialize the @@ -2487,12 +2535,11 @@ if (fully_qualified_name != NULL) *fully_qualified_name = host->name; dns_init((whichrrs & HOST_FIND_QUALIFY_SINGLE) != 0, (whichrrs & HOST_FIND_SEARCH_PARENTS) != 0, - dnssec_request - ); + dnssec_request); host_find_failed_syntax = FALSE; /* First, if requested, look for SRV records. The service name is given; we -assume TCP progocol. DNS domain names are constrained to a maximum of 256 +assume TCP protocol. DNS domain names are constrained to a maximum of 256 characters, so the code below should be safe. */ if ((whichrrs & HOST_FIND_BY_SRV) != 0) @@ -2511,7 +2558,13 @@ dnssec = DS_UNK; lookup_dnssec_authenticated = NULL; - rc = dns_lookup(&dnsa, buffer, ind_type, &temp_fully_qualified_name); + rc = dns_lookup_timerwrap(&dnsa, buffer, ind_type, CUSS &temp_fully_qualified_name); + + DEBUG(D_dns) + if ((dnssec_request || dnssec_require) + & !dns_is_secure(&dnsa) + & dns_is_aa(&dnsa)) + debug_printf("DNS lookup of %.256s (SRV) requested AD, but got AA\n", host->name); if (dnssec_request) { @@ -2536,8 +2589,8 @@ if (rc == DNS_FAIL || rc == DNS_AGAIN) { #ifndef STAND_ALONE - if (match_isinlist(host->name, &srv_fail_domains, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK) + if (match_isinlist(host->name, CUSS &srv_fail_domains, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) != OK) #endif { yield = HOST_FIND_AGAIN; goto out; } DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " @@ -2557,14 +2610,25 @@ ind_type = T_MX; dnssec = DS_UNK; lookup_dnssec_authenticated = NULL; - rc = dns_lookup(&dnsa, host->name, ind_type, fully_qualified_name); + rc = dns_lookup_timerwrap(&dnsa, host->name, ind_type, fully_qualified_name); + + DEBUG(D_dns) + if ((dnssec_request || dnssec_require) + & !dns_is_secure(&dnsa) + & dns_is_aa(&dnsa)) + debug_printf("DNS lookup of %.256s (MX) requested AD, but got AA\n", host->name); if (dnssec_request) { if (dns_is_secure(&dnsa)) - { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } + { + DEBUG(D_host_lookup) debug_printf("%s MX DNSSEC\n", host->name); + dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; + } else - { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; } + { + dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; + } } switch (rc) @@ -2578,13 +2642,13 @@ log_write(L_host_lookup_failed, LOG_MAIN, "dnssec fail on MX for %.256s", host->name); rc = DNS_FAIL; - /*FALLTRHOUGH*/ + /*FALLTHROUGH*/ case DNS_FAIL: case DNS_AGAIN: #ifndef STAND_ALONE - if (match_isinlist(host->name, &mx_fail_domains, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK) + if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) != OK) #endif { yield = HOST_FIND_AGAIN; goto out; } DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " @@ -2609,19 +2673,11 @@ last = host; /* End of local chainlet */ host->mx = MX_NONE; host->port = PORT_NONE; - dnssec = DS_UNK; + host->dnssec = DS_UNK; lookup_dnssec_authenticated = NULL; rc = set_address_from_dns(host, &last, ignore_target_hosts, FALSE, fully_qualified_name, dnssec_request, dnssec_require); - if (dnssec_request) - { - if (dns_is_secure(&dnsa)) - { dnssec = DS_YES; lookup_dnssec_authenticated = US"yes"; } - else - { dnssec = DS_NO; lookup_dnssec_authenticated = US"no"; } - } - /* If one or more address records have been found, check that none of them are local. Since we know the host items all have their IP addresses inserted, host_scan_for_local_hosts() can only return HOST_FOUND or @@ -3051,8 +3107,9 @@ yield); for (h = host; h != last->next; h = h->next) { - debug_printf(" %s %s MX=%d ", h->name, - (h->address == NULL)? US"" : h->address, h->mx); + debug_printf(" %s %s MX=%d %s", h->name, + !h->address ? US"" : h->address, h->mx, + h->dnssec == DS_YES ? US"DNSSEC " : US""); if (h->port != PORT_NONE) debug_printf("port=%d ", h->port); if (h->status >= hstatus_unusable) debug_printf("*"); debug_printf("\n"); @@ -3065,9 +3122,6 @@ return yield; } - - - /************************************************* ************************************************** * Stand-alone test program * @@ -3158,6 +3212,7 @@ else { int flags = whichrrs; + dnssec d; h.name = buffer; h.next = NULL; @@ -3170,12 +3225,13 @@ if (qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE; if (search_parents) flags |= HOST_FIND_SEARCH_PARENTS; + d.request = request_dnssec ? &h.name : NULL; + d.require = require_dnssec ? &h.name : NULL; + rc = byname ? host_find_byname(&h, NULL, flags, &fully_qualified_name, TRUE) : host_find_bydns(&h, NULL, flags, US"smtp", NULL, NULL, - request_dnssec ? &h.name : NULL, - require_dnssec ? &h.name : NULL, - &fully_qualified_name, NULL); + &d, &fully_qualified_name, NULL); if (rc == HOST_FIND_FAILED) printf("Failed\n"); else if (rc == HOST_FIND_AGAIN) printf("Again\n"); diff -Nru exim4-4.84/src/imap_utf7.c exim4-4.86~RC4/src/imap_utf7.c --- exim4-4.84/src/imap_utf7.c 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/imap_utf7.c 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,210 @@ +#include "exim.h" + +#ifdef EXPERIMENTAL_INTERNATIONAL + +uschar * +imap_utf7_encode(uschar *string, const uschar *charset, uschar sep, + uschar *specials, uschar **error) +{ +static uschar encode_base64[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; +int ptr = 0; +int size = 0; +size_t slen; +uschar *sptr, *yield = NULL; +int i = 0, j; /* compiler quietening */ +uschar c = 0; /* compiler quietening */ +BOOL base64mode = FALSE; +BOOL lastsep = FALSE; +uschar utf16buf[256]; +uschar *utf16ptr; +uschar *s; +uschar outbuf[256]; +uschar *outptr = outbuf; +#if HAVE_ICONV +iconv_t icd; +#endif + +if (!specials) specials = US""; + +/* Pass over the string. If it consists entirely of "normal" characters + (possibly with leading seps), return it as is. */ +for (s = string; *s; s++) + { + if (s == string && *s == sep) + string++; + if ( *s >= 0x7f + || *s < 0x20 + || strchr("./&", *s) + || *s == sep + || Ustrchr(specials, *s) + ) + break; + } + +if (!*s) + return string; + +sptr = string; +slen = Ustrlen(string); + +#if HAVE_ICONV +if ((icd = iconv_open("UTF-16BE", CCS charset)) == (iconv_t)-1) + { + *error = string_sprintf( + "imapfolder: iconv_open(\"UTF-16BE\", \"%s\") failed: %s%s", + charset, strerror(errno), + errno == EINVAL ? " (maybe unsupported conversion)" : ""); + return NULL; + } +#endif + +while (slen > 0) + { +#if HAVE_ICONV + size_t left = sizeof(utf16buf); + utf16ptr = utf16buf; + + if ( iconv(icd, (ICONV_ARG2_TYPE)&sptr, &slen, CSS &utf16ptr, &left) + == (size_t)-1 + && errno != E2BIG + ) + { + *error = string_sprintf("imapfolder: iconv() failed to convert from %s: %s", + charset, strerror(errno)); + iconv_close(icd); + return NULL; + } +#else + for (utf16ptr = utf16buf; + slen > 0 && (utf16ptr - utf16buf) < sizeof(utf16buf); + utf16ptr += 2, slen--, sptr++) + { + *utf16ptr = *sptr; + *(utf16ptr+1) = '\0'; + } +#endif + + s = utf16buf; + while (s < utf16ptr) + { + /* Now encode utf16buf as modified UTF-7 */ + if ( s[0] != 0 + || s[1] >= 0x7f + || s[1] < 0x20 + || (Ustrchr(specials, s[1]) && s[1] != sep) + ) + { + lastsep = FALSE; + /* Encode as modified BASE64 */ + if (!base64mode) + { + *outptr++ = '&'; + base64mode = TRUE; + i = 0; + } + + for (j = 0; j < 2; j++, s++) switch (i++) + { + case 0: + /* Top 6 bits of the first octet */ + *outptr++ = encode_base64[(*s >> 2) & 0x3F]; + c = (*s & 0x03); break; + case 1: + /* Bottom 2 bits of the first octet, and top 4 bits of the second */ + *outptr++ = encode_base64[(c << 4) | ((*s >> 4) & 0x0F)]; + c = (*s & 0x0F); break; + case 2: + /* Bottom 4 bits of the second octet and top 2 bits of the third */ + *outptr++ = encode_base64[(c << 2) | ((*s >> 6) & 0x03)]; + /* Bottom 6 bits of the third octet */ + *outptr++ = encode_base64[*s & 0x3F]; + i = 0; + } + } + + else if ( (s[1] != '.' && s[1] != '/') + || s[1] == sep + ) + { + /* Encode as self (almost) */ + if (base64mode) + { + switch (i) + { + case 1: + /* Remaining bottom 2 bits of the last octet */ + *outptr++ = encode_base64[c << 4]; + break; + case 2: + /* Remaining bottom 4 bits of the last octet */ + *outptr++ = encode_base64[c << 2]; + } + *outptr++ = '-'; + base64mode = FALSE; + } + + if (*++s == sep) + { + if (!lastsep) + { + *outptr++ = '.'; + lastsep = TRUE; + } + } + else + { + *outptr++ = *s; + if (*s == '&') + *outptr++ = '-'; + lastsep = FALSE; + } + + s++; + } + else + { + *error = string_sprintf("imapfolder: illegal character '%c'", s[1]); + if (yield) store_reset(yield); + return NULL; + } + + if (outptr > outbuf + sizeof(outbuf) - 3) + { + yield = string_cat(yield, &size, &ptr, outbuf, outptr - outbuf); + outptr = outbuf; + } + + } + } /* End of input string */ + +if (base64mode) + { + switch (i) + { + case 1: + /* Remaining bottom 2 bits of the last octet */ + *outptr++ = encode_base64[c << 4]; + break; + case 2: + /* Remaining bottom 4 bits of the last octet */ + *outptr++ = encode_base64[c << 2]; + } + *outptr++ = '-'; + } + +#if HAVE_ICONV +iconv_close(icd); +#endif + +yield = string_cat(yield, &size, &ptr, outbuf, outptr - outbuf); +if (yield[ptr-1] == '.') + ptr--; +yield[ptr] = '\0'; + +return yield; +} + +#endif /* whole file */ +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/ip.c exim4-4.86~RC4/src/ip.c --- exim4-4.84/src/ip.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/ip.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for doing things with sockets. With the advent of IPv6 this has @@ -64,11 +64,11 @@ */ static void -ip_addrinfo(uschar *address, struct sockaddr_in6 *saddr) +ip_addrinfo(const uschar *address, struct sockaddr_in6 *saddr) { #ifdef IPV6_USE_INET_PTON - if (inet_pton(AF_INET6, CS address, &saddr->sin6_addr) != 1) + if (inet_pton(AF_INET6, CCS address, &saddr->sin6_addr) != 1) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an " "IP address", address); saddr->sin6_family = AF_INET6; @@ -81,7 +81,7 @@ hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; - if ((rc = getaddrinfo(CS address, NULL, &hints, &res)) != 0 || res == NULL) + if ((rc = getaddrinfo(CCS address, NULL, &hints, &res)) != 0 || res == NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an " "IP address: %s", address, (rc == 0)? "NULL result returned" : gai_strerror(rc)); @@ -97,24 +97,11 @@ * Bind socket to interface and port * *************************************************/ -/* This function binds a socket to a local interface address and port. For a -wildcard IPv6 bind, the address is ":". - -Arguments: - sock the socket - af AF_INET or AF_INET6 - the socket type - address the IP address, in text form - port the IP port (host order) - -Returns: the result of bind() -*/ - int -ip_bind(int sock, int af, uschar *address, int port) +ip_addr(void * sin_, int af, const uschar * address, int port) { -int s_len; -union sockaddr_46 sin; -memset(&sin, 0, sizeof(sin)); +union sockaddr_46 * sin = sin_; +memset(sin, 0, sizeof(*sin)); /* Setup code when using an IPv6 socket. The wildcard address is ":", to ensure an IPv6 socket is used. */ @@ -124,15 +111,13 @@ { if (address[0] == ':' && address[1] == 0) { - sin.v6.sin6_family = AF_INET6; - sin.v6.sin6_addr = in6addr_any; + sin->v6.sin6_family = AF_INET6; + sin->v6.sin6_addr = in6addr_any; } else - { - ip_addrinfo(address, &sin.v6); /* Panic-dies on error */ - } - sin.v6.sin6_port = htons(port); - s_len = sizeof(sin.v6); + ip_addrinfo(address, &sin->v6); /* Panic-dies on error */ + sin->v6.sin6_port = htons(port); + return sizeof(sin->v6); } else #else /* HAVE_IPv6 */ @@ -142,17 +127,34 @@ /* Setup code when using IPv4 socket. The wildcard address is "". */ { - sin.v4.sin_family = AF_INET; - sin.v4.sin_port = htons(port); - s_len = sizeof(sin.v4); - if (address[0] == 0) - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY; - else - sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CS address); + sin->v4.sin_family = AF_INET; + sin->v4.sin_port = htons(port); + sin->v4.sin_addr.s_addr = address[0] == 0 + ? (S_ADDR_TYPE)INADDR_ANY + : (S_ADDR_TYPE)inet_addr(CS address); + return sizeof(sin->v4); } +} + + + +/* This function binds a socket to a local interface address and port. For a +wildcard IPv6 bind, the address is ":". + +Arguments: + sock the socket + af AF_INET or AF_INET6 - the socket type + address the IP address, in text form + port the IP port (host order) -/* Now we can call the bind() function */ +Returns: the result of bind() +*/ +int +ip_bind(int sock, int af, uschar *address, int port) +{ +union sockaddr_46 sin; +int s_len = ip_addr(&sin, af, address, port); return bind(sock, (struct sockaddr *)&sin, s_len); } @@ -178,7 +180,7 @@ */ int -ip_connect(int sock, int af, uschar *address, int port, int timeout) +ip_connect(int sock, int af, const uschar *address, int port, int timeout) { struct sockaddr_in s_in4; struct sockaddr *s_ptr; @@ -208,7 +210,7 @@ memset(&s_in4, 0, sizeof(s_in4)); s_in4.sin_family = AF_INET; s_in4.sin_port = htons(port); - s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CS address); + s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CCS address); s_ptr = (struct sockaddr *)&s_in4; s_len = sizeof(s_in4); } @@ -226,14 +228,11 @@ can't think of any other way of doing this. It converts a connection refused into a timeout if the timeout is set to 999999. */ -if (running_in_test_harness) +if (running_in_test_harness && save_errno == ECONNREFUSED && timeout == 999999) { - if (save_errno == ECONNREFUSED && timeout == 999999) - { - rc = -1; - save_errno = EINTR; - sigalrm_seen = TRUE; - } + rc = -1; + save_errno = EINTR; + sigalrm_seen = TRUE; } /* Success */ @@ -243,7 +242,7 @@ /* A failure whose error code is "Interrupted system call" is in fact an externally applied timeout if the signal handler has been run. */ -errno = (save_errno == EINTR && sigalrm_seen)? ETIMEDOUT : save_errno; +errno = save_errno == EINTR && sigalrm_seen ? ETIMEDOUT : save_errno; return -1; } @@ -310,8 +309,8 @@ else { shost.name = string_copy(hostname); - if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, - FALSE) != HOST_FOUND) + if (host_find_byname(&shost, NULL, HOST_FIND_QUALIFY_SINGLE, + NULL, FALSE) != HOST_FOUND) { *errstr = string_sprintf("no IP address found for host %s", shost.name); return -1; @@ -337,7 +336,8 @@ { if (fd != fd6) close(fd6); if (fd != fd4) close(fd4); - if (connhost) { + if (connhost) + { h->port = port; *connhost = *h; connhost->next = NULL; @@ -346,14 +346,70 @@ } } -*errstr = string_sprintf("failed to connect to %s: " - "couldn't connect to any host: %s", hostname, strerror(errno)); +*errstr = string_sprintf("failed to connect to any address for %s: %s", + hostname, strerror(errno)); bad: close(fd4); close(fd6); return -1; } +int +ip_tcpsocket(const uschar * hostport, uschar ** errstr, int tmo) +{ +int scan; +uschar hostname[256]; +unsigned int portlow, porthigh; + +/* extract host and port part */ +scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh); +if (scan != 3) + { + if (scan != 2) + { + *errstr = string_sprintf("invalid socket '%s'", hostport); + return -1; + } + porthigh = portlow; + } + +return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh, + tmo, NULL, errstr); +} + +int +ip_unixsocket(const uschar * path, uschar ** errstr) +{ +int sock; +struct sockaddr_un server; + +if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + *errstr = US"can't open UNIX socket."; + return -1; + } + +server.sun_family = AF_UNIX; +Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1); +server.sun_path[sizeof(server.sun_path)-1] = '\0'; +if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) + { + int err = errno; + (void)close(sock); + *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s", + path, strerror(err)); + return -1; + } +return sock; +} + +int +ip_streamsocket(const uschar * spec, uschar ** errstr, int tmo) +{ +return *spec == '/' + ? ip_unixsocket(spec, errstr) : ip_tcpsocket(spec, errstr, tmo); +} + /************************************************* * Set keepalive on a socket * *************************************************/ @@ -369,7 +425,7 @@ */ void -ip_keepalive(int sock, uschar *address, BOOL torf) +ip_keepalive(int sock, const uschar *address, BOOL torf) { int fodder = 1; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, @@ -384,39 +440,37 @@ * Receive from a socket with timeout * *************************************************/ -/* The timeout is implemented using select(), and we loop to cover select() -getting interrupted, and the possibility of select() returning with a positive -result but no ready descriptor. Is this in fact possible? - +/* Arguments: - sock the socket - buffer to read into - bufsize the buffer size - timeout the timeout - -Returns: > 0 => that much data read - <= 0 on error or EOF; errno set - zero for EOF + fd the file descriptor + timeout the timeout, seconds +Returns: TRUE => ready for i/o + FALSE => timed out, or other error */ - -int -ip_recv(int sock, uschar *buffer, int buffsize, int timeout) +BOOL +fd_ready(int fd, int timeout) { fd_set select_inset; struct timeval tv; time_t start_recv = time(NULL); int rc; +if (timeout <= 0) + { + errno = ETIMEDOUT; + return FALSE; + } /* Wait until the socket is ready */ -for (;;) +do { FD_ZERO (&select_inset); - FD_SET (sock, &select_inset); + FD_SET (fd, &select_inset); tv.tv_sec = timeout; tv.tv_usec = 0; - DEBUG(D_transport) debug_printf("waiting for data on socket\n"); - rc = select(sock + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv); + /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/ + rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv); /* If some interrupt arrived, just retry. We presume this to be rare, but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes @@ -440,13 +494,36 @@ if (rc <= 0) { errno = ETIMEDOUT; - return -1; + return FALSE; } /* If the socket is ready, break out of the loop. */ - - if (FD_ISSET(sock, &select_inset)) break; } +while (!FD_ISSET(fd, &select_inset)); +return TRUE; +} + +/* The timeout is implemented using select(), and we loop to cover select() +getting interrupted, and the possibility of select() returning with a positive +result but no ready descriptor. Is this in fact possible? + +Arguments: + sock the socket + buffer to read into + bufsize the buffer size + timeout the timeout + +Returns: > 0 => that much data read + <= 0 on error or EOF; errno set - zero for EOF +*/ + +int +ip_recv(int sock, uschar *buffer, int buffsize, int timeout) +{ +int rc; + +if (!fd_ready(sock, timeout)) + return -1; /* The socket is ready, read from it (via TLS if it's active). On EOF (i.e. close down of the connection), set errno to zero; otherwise leave it alone. */ @@ -625,13 +702,9 @@ return TRUE; } else if (c > 0) - { first = middle + 1; - } else - { last = middle; - } } return FALSE; } @@ -646,3 +719,5 @@ /* End of ip.c */ +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/local_scan.h exim4-4.86~RC4/src/local_scan.h --- exim4-4.84/src/local_scan.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/local_scan.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This file is the header that is the only Exim header to be included in the @@ -128,10 +128,8 @@ uschar *address; /* the recipient address */ int pno; /* parent number for "one_time" alias, or -1 */ uschar *errors_to; /* the errors_to address or NULL */ -#ifdef EXPERIMENTAL_DSN uschar *orcpt; /* DSN orcpt */ int dsn_flags; /* DSN flags */ -#endif #ifdef EXPERIMENTAL_BRIGHTMAIL uschar *bmi_optin; #endif @@ -191,7 +189,7 @@ extern void smtp_printf(const char *, ...) PRINTF_FUNCTION(1,2); extern void smtp_vprintf(const char *, va_list); extern uschar *string_copy(const uschar *); -extern uschar *string_copyn(uschar *, int); +extern uschar *string_copyn(const uschar *, int); extern uschar *string_sprintf(const char *, ...) ALMOST_PRINTF(1,2); /* End of local_scan.h */ diff -Nru exim4-4.84/src/log.c exim4-4.86~RC4/src/log.c --- exim4-4.84/src/log.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/log.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for writing log files. The code for maintaining datestamped @@ -556,6 +556,23 @@ } + +static void +set_file_path(void) +{ +int sep = ':'; /* Fixed separator - outside use */ +uschar *t; +const uschar *tt = US LOG_FILE_PATH; +while ((t = string_nextinlist(&tt, &sep, log_buffer, LOG_BUFFER_SIZE))) + { + if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; + file_path = string_copy(t); + break; + } +} + + + /************************************************* * Write message to log file * *************************************************/ @@ -668,13 +685,13 @@ /* If nothing has been set, don't waste effort... the default values for the statics are file_path="" and logging_mode = LOG_MODE_FILE. */ - if (log_file_path[0] != 0) + if (*log_file_path) { int sep = ':'; /* Fixed separator - outside use */ uschar *s; - uschar *ss = log_file_path; + const uschar *ss = log_file_path; logging_mode = 0; - while ((s = string_nextinlist(&ss,&sep,log_buffer,LOG_BUFFER_SIZE)) != NULL) + while ((s = string_nextinlist(&ss, &sep, log_buffer, LOG_BUFFER_SIZE))) { if (Ustrcmp(s, "syslog") == 0) logging_mode |= LOG_MODE_SYSLOG; @@ -685,10 +702,8 @@ /* If a non-empty path is given, use it */ - if (s[0] != 0) - { + if (*s) file_path = string_copy(s); - } /* If the path is empty, we want to use the first non-empty, non- syslog item in LOG_FILE_PATH, if there is one, since the value of @@ -696,17 +711,7 @@ use the ultimate default in the spool directory. */ else - { - uschar *t; - uschar *tt = US LOG_FILE_PATH; - while ((t = string_nextinlist(&tt,&sep,log_buffer,LOG_BUFFER_SIZE)) - != NULL) - { - if (Ustrcmp(t, "syslog") == 0 || t[0] == 0) continue; - file_path = string_copy(t); - break; - } - } /* Empty item in log_file_path */ + set_file_path(); /* Empty item in log_file_path */ } /* First non-syslog item in log_file_path */ } /* Scan of log_file_path */ } @@ -729,10 +734,8 @@ should work since we have now set up the routing. */ if (multiple) - { log_write(0, LOG_MAIN|LOG_PANIC, "More than one path given in log_file_path: using %s", file_path); - } } /* If debugging, show all log entries, but don't show headers. Do it all @@ -1337,6 +1340,12 @@ debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG); } +/* When activating from a transport process we may never have logged at all +resulting in certain setup not having been done. Hack this for now so we +do not segfault; note that nondefault log locations will not work */ + +if (!*file_path) set_file_path(); + open_log(&fd, lt_debug, tag_name); if (fd != -1) diff -Nru exim4-4.84/src/lookupapi.h exim4-4.86~RC4/src/lookupapi.h --- exim4-4.84/src/lookupapi.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookupapi.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -30,7 +30,7 @@ int (*find)( /* find function */ void *, /* handle */ uschar *, /* file name or NULL */ - uschar *, /* key or query */ + const uschar *, /* key or query */ int, /* length of key or query */ uschar **, /* for returning answer */ uschar **, /* for error message */ diff -Nru exim4-4.84/src/lookups/cdb.c exim4-4.86~RC4/src/lookups/cdb.c --- exim4-4.84/src/lookups/cdb.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/cdb.c 2015-06-27 17:01:28.000000000 +0200 @@ -18,6 +18,9 @@ * Changed over to using unsigned chars * Makes use of lf_check_file() for file checking * -------------------------------------------------------------- + * Modified by The Exim Maintainers 2015: + * const propagation + * -------------------------------------------------------------- * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -272,7 +275,7 @@ static int cdb_find(void *handle, uschar *filename, - uschar *keystring, + const uschar *keystring, int key_len, uschar **result, uschar **errmsg, diff -Nru exim4-4.84/src/lookups/dbmdb.c exim4-4.86~RC4/src/lookups/dbmdb.c --- exim4-4.84/src/lookups/dbmdb.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/dbmdb.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -86,7 +86,7 @@ the keylength in order to include the terminating zero. */ static int -dbmdb_find(void *handle, uschar *filename, uschar *keystring, int length, +dbmdb_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { EXIM_DB *d = (EXIM_DB *)handle; @@ -119,7 +119,7 @@ /* See local README for interface description */ int -static dbmnz_find(void *handle, uschar *filename, uschar *keystring, int length, +static dbmnz_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { return dbmdb_find(handle, filename, keystring, length-1, result, errmsg, @@ -139,11 +139,11 @@ */ static int -dbmjz_find(void *handle, uschar *filename, uschar *keystring, int length, +dbmjz_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { uschar *key_item, *key_buffer, *key_p; -uschar *key_elems = keystring; +const uschar *key_elems = keystring; int buflen, bufleft, key_item_len, sep = 0; /* To a first approximation, the size of the lookup key needs to be about, diff -Nru exim4-4.84/src/lookups/dnsdb.c exim4-4.86~RC4/src/lookups/dnsdb.c --- exim4-4.84/src/lookups/dnsdb.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/dnsdb.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -34,9 +34,6 @@ #if HAVE_IPV6 "a+", "aaaa", - #ifdef SUPPORT_A6 - "a6", - #endif #endif "cname", "csa", @@ -44,6 +41,7 @@ "mxh", "ns", "ptr", + "soa", "spf", "srv", "tlsa", @@ -56,9 +54,6 @@ #if HAVE_IPV6 T_ADDRESSES, /* Private type for AAAA + A */ T_AAAA, - #ifdef SUPPORT_A6 - T_A6, - #endif #endif T_CNAME, T_CSA, /* Private type for "Client SMTP Authorization". */ @@ -66,6 +61,7 @@ T_MXH, /* Private type for "MX hostnames" */ T_NS, T_PTR, + T_SOA, T_SPF, T_SRV, T_TLSA, @@ -108,26 +104,33 @@ is output. Similarly for "SPF" records, but the default for joining multiple items in one SPF record is the empty string, for direct concatenation. -(c) If the next sequence of characters is 'defer_FOO' followed by a comma, -the defer behaviour is set to FOO. The possible behaviours are: 'strict', where -any defer causes the whole lookup to defer; 'lax', where a defer causes the -whole lookup to defer only if none of the DNS queries succeeds; and 'never', -where all defers are as if the lookup failed. The default is 'lax'. +(c) Options, all comma-terminated, in any order. Any unrecognised option +terminates option processing. Recognised options are: -(d) Another optional comma-sep field: 'dnssec_FOO', with 'strict', 'lax' -and 'never' (default); can appear before or after (c). The meanings are +- 'defer_FOO': set the defer behaviour to FOO. The possible behaviours are: +'strict', where any defer causes the whole lookup to defer; 'lax', where a defer +causes the whole lookup to defer only if none of the DNS queries succeeds; and +'never', where all defers are as if the lookup failed. The default is 'lax'. + +- 'dnssec_FOO', with 'strict', 'lax' and 'never' (default). The meanings are require, try and don't-try dnssec respectively. -(e) If the next sequence of characters is a sequence of letters and digits +- 'retrans_VAL', set the timeout value. VAL is an Exim time specification +(eg "5s"). The default is set by the main configuration option 'dns_retrans'. + +- 'retry_VAL', set the retry count on timeouts. VAL is an integer. The +default is set by the main configuration option "dns_retry". + +(d) If the next sequence of characters is a sequence of letters and digits followed by '=', it is interpreted as the name of the DNS record type. The default is "TXT". -(f) Then there follows list of domain names. This is a generalized Exim list, +(e) Then there follows list of domain names. This is a generalized Exim list, which may start with '<' in order to set a specific separator. The default separator, as always, is colon. */ static int -dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length, +dnsdb_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { int rc; @@ -136,12 +139,13 @@ int sep = 0; int defer_mode = PASS; int dnssec_mode = OK; +int save_retrans = dns_retrans; +int save_retry = dns_retry; int type; int failrc = FAIL; -uschar *outsep = US"\n"; -uschar *outsep2 = NULL; +const uschar *outsep = CUS"\n"; +const uschar *outsep2 = NULL; uschar *equals, *domain, *found; -uschar buffer[256]; /* Because we're the working in the search pool, we try to reclaim as much store as possible later, so we preallocate the result here */ @@ -180,58 +184,62 @@ /* Check for a modifier keyword. */ -while ( strncmpic(keystring, US"defer_", 6) == 0 - || strncmpic(keystring, US"dnssec_", 7) == 0 - ) +for (;;) { if (strncmpic(keystring, US"defer_", 6) == 0) { keystring += 6; if (strncmpic(keystring, US"strict", 6) == 0) - { - defer_mode = DEFER; - keystring += 6; - } + { defer_mode = DEFER; keystring += 6; } else if (strncmpic(keystring, US"lax", 3) == 0) - { - defer_mode = PASS; - keystring += 3; - } + { defer_mode = PASS; keystring += 3; } else if (strncmpic(keystring, US"never", 5) == 0) - { - defer_mode = OK; - keystring += 5; - } + { defer_mode = OK; keystring += 5; } else { *errmsg = US"unsupported dnsdb defer behaviour"; return DEFER; } } - else + else if (strncmpic(keystring, US"dnssec_", 7) == 0) { keystring += 7; if (strncmpic(keystring, US"strict", 6) == 0) - { - dnssec_mode = DEFER; - keystring += 6; - } + { dnssec_mode = DEFER; keystring += 6; } else if (strncmpic(keystring, US"lax", 3) == 0) + { dnssec_mode = PASS; keystring += 3; } + else if (strncmpic(keystring, US"never", 5) == 0) + { dnssec_mode = OK; keystring += 5; } + else { - dnssec_mode = PASS; - keystring += 3; + *errmsg = US"unsupported dnsdb dnssec behaviour"; + return DEFER; } - else if (strncmpic(keystring, US"never", 5) == 0) + } + else if (strncmpic(keystring, US"retrans_", 8) == 0) + { + int timeout_sec; + if ((timeout_sec = readconf_readtime(keystring += 8, ',', FALSE)) <= 0) { - dnssec_mode = OK; - keystring += 5; + *errmsg = US"unsupported dnsdb timeout value"; + return DEFER; } - else + dns_retrans = timeout_sec; + while (*keystring != ',') keystring++; + } + else if (strncmpic(keystring, US"retry_", 6) == 0) + { + int retries; + if ((retries = (int)strtol(CCS keystring + 6, CSS &keystring, 0)) < 0) { - *errmsg = US"unsupported dnsdb dnssec behaviour"; + *errmsg = US"unsupported dnsdb retry count"; return DEFER; } + dns_retry = retries; } + else + break; + while (isspace(*keystring)) keystring++; if (*keystring++ != ',') { @@ -295,15 +303,19 @@ /* SPF strings should be concatenated without a separator, thus make it the default if not defined (see RFC 4408 section 3.1.3). Multiple SPF records are forbidden (section 3.1.2) but are currently -not handled specially, thus they are concatenated with \n by default. */ +not handled specially, thus they are concatenated with \n by default. +MX priority and value are space-separated by default. +SRV and TLSA record parts are space-separated by default. */ -if (type == T_SPF && outsep2 == NULL) - outsep2 = US""; +if (!outsep2) switch(type) + { + case T_SPF: outsep2 = US""; break; + case T_SRV: case T_MX: case T_TLSA: outsep2 = US" "; break; + } /* Now scan the list and do a lookup for each item */ -while ((domain = string_nextinlist(&keystring, &sep, buffer, sizeof(buffer))) - != NULL) +while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) { uschar rbuffer[256]; int searchtype = (type == T_CSA)? T_SRV : /* record type we want */ @@ -340,19 +352,13 @@ #if HAVE_IPV6 if (type == T_ADDRESSES) /* NB cannot happen unless HAVE_IPV6 */ { - if (searchtype == T_ADDRESSES) -# if defined(SUPPORT_A6) - searchtype = T_A6; -# else - searchtype = T_AAAA; -# endif - else if (searchtype == T_A6) searchtype = T_AAAA; + if (searchtype == T_ADDRESSES) searchtype = T_AAAA; else if (searchtype == T_AAAA) searchtype = T_A; - rc = dns_special_lookup(&dnsa, domain, searchtype, &found); + rc = dns_special_lookup(&dnsa, domain, searchtype, CUSS &found); } else #endif - rc = dns_special_lookup(&dnsa, domain, type, &found); + rc = dns_special_lookup(&dnsa, domain, type, CUSS &found); lookup_dnssec_authenticated = dnssec_mode==OK ? NULL : dns_is_secure(&dnsa) ? US"yes" : US"no"; @@ -364,6 +370,8 @@ { if (defer_mode == DEFER) { + dns_retrans = save_retrans; + dns_retry = save_retry; dns_init(FALSE, FALSE, FALSE); /* clr dnssec bit */ return DEFER; /* always defer */ } @@ -380,19 +388,10 @@ { if (rr->type != searchtype) continue; - /* There may be several addresses from an A6 record. Put the configured - separator between them, just as for between several records. However, A6 - support is not normally configured these days. */ - - if (type == T_A || - #ifdef SUPPORT_A6 - type == T_A6 || - #endif - type == T_AAAA || - type == T_ADDRESSES) + if (type == T_A || type == T_AAAA || type == T_ADDRESSES) { dns_address *da; - for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next) + for (da = dns_address_from_rr(&dnsa, rr); da; da = da->next) { if (ptr != 0) yield = string_cat(yield, &size, &ptr, outsep, 1); yield = string_cat(yield, &size, &ptr, da->address, @@ -435,83 +434,88 @@ uint16_t i, payload_length; uschar s[MAX_TLSA_EXPANDED_SIZE]; uschar * sp = s; - uschar *p = (uschar *)(rr->data); + uschar * p = US rr->data; usage = *p++; selector = *p++; matching_type = *p++; /* What's left after removing the first 3 bytes above */ payload_length = rr->size - 3; - sp += sprintf(CS s, "%d %d %d ", usage, selector, matching_type); + sp += sprintf(CS s, "%d%c%d%c%d%c", usage, *outsep2, + selector, *outsep2, matching_type, *outsep2); /* Now append the cert/identifier, one hex char at a time */ for (i=0; i < payload_length && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4); i++) - { sp += sprintf(CS sp, "%02x", (unsigned char)p[i]); - } + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); } - else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SRV */ + else /* T_CNAME, T_CSA, T_MX, T_MXH, T_NS, T_PTR, T_SOA, T_SRV */ { int priority, weight, port; uschar s[264]; - uschar *p = (uschar *)(rr->data); - - if (type == T_MXH) - { - /* mxh ignores the priority number and includes only the hostnames */ - GETSHORT(priority, p); - } - else if (type == T_MX) - { - GETSHORT(priority, p); - sprintf(CS s, "%d ", priority); - yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); - } - else if (type == T_SRV) - { - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); - sprintf(CS s, "%d %d %d ", priority, weight, port); - yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); - } - else if (type == T_CSA) - { - /* See acl_verify_csa() for more comments about CSA. */ - - GETSHORT(priority, p); - GETSHORT(weight, p); - GETSHORT(port, p); - - if (priority != 1) continue; /* CSA version must be 1 */ - - /* If the CSA record we found is not the one we asked for, analyse - the subdomain assertions in the port field, else analyse the direct - authorization status in the weight field. */ - - if (found != domain) - { - if (port & 1) *s = 'X'; /* explicit authorization required */ - else *s = '?'; /* no subdomain assertions here */ - } - else - { - if (weight < 2) *s = 'N'; /* not authorized */ - else if (weight == 2) *s = 'Y'; /* authorized */ - else if (weight == 3) *s = '?'; /* unauthorizable */ - else continue; /* invalid */ - } + uschar * p = US rr->data; - s[1] = ' '; - yield = string_cat(yield, &size, &ptr, s, 2); - } + switch (type) + { + case T_MXH: + /* mxh ignores the priority number and includes only the hostnames */ + GETSHORT(priority, p); + break; + + case T_MX: + GETSHORT(priority, p); + sprintf(CS s, "%d%c", priority, *outsep2); + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + break; + + case T_SRV: + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + sprintf(CS s, "%d%c%d%c%d%c", priority, *outsep2, + weight, *outsep2, port, *outsep2); + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + break; + + case T_CSA: + /* See acl_verify_csa() for more comments about CSA. */ + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + + if (priority != 1) continue; /* CSA version must be 1 */ + + /* If the CSA record we found is not the one we asked for, analyse + the subdomain assertions in the port field, else analyse the direct + authorization status in the weight field. */ + + if (Ustrcmp(found, domain) != 0) + { + if (port & 1) *s = 'X'; /* explicit authorization required */ + else *s = '?'; /* no subdomain assertions here */ + } + else + { + if (weight < 2) *s = 'N'; /* not authorized */ + else if (weight == 2) *s = 'Y'; /* authorized */ + else if (weight == 3) *s = '?'; /* unauthorizable */ + else continue; /* invalid */ + } + + s[1] = ' '; + yield = string_cat(yield, &size, &ptr, s, 2); + break; + + default: + break; + } /* GETSHORT() has advanced the pointer to the target domain. */ rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p, - (DN_EXPAND_ARG4_TYPE)(s), sizeof(s)); + (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); /* If an overlong response was received, the data will have been truncated and dn_expand may fail. */ @@ -523,6 +527,32 @@ break; } else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + + if (type == T_SOA && outsep2 != NULL) + { + unsigned long serial, refresh, retry, expire, minimum; + + p += rc; + yield = string_cat(yield, &size, &ptr, outsep2, 1); + + rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p, + (DN_EXPAND_ARG4_TYPE)s, sizeof(s)); + if (rc < 0) + { + log_write(0, LOG_MAIN, "responsible-mailbox truncated: type=%s " + "domain=%s", dns_text_type(type), domain); + break; + } + else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + + p += rc; + GETLONG(serial, p); GETLONG(refresh, p); + GETLONG(retry, p); GETLONG(expire, p); GETLONG(minimum, p); + sprintf(CS s, "%c%lu%c%lu%c%lu%c%lu%c%lu", + *outsep2, serial, *outsep2, refresh, + *outsep2, retry, *outsep2, expire, *outsep2, minimum); + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); + } } } /* Loop for list of returned records */ @@ -538,6 +568,8 @@ /* If ptr == 0 we have not found anything. Otherwise, insert the terminating zero and return the result. */ +dns_retrans = save_retrans; +dns_retry = save_retry; dns_init(FALSE, FALSE, FALSE); /* clear the dnssec bit for getaddrbyname */ if (ptr == 0) return failrc; diff -Nru exim4-4.84/src/lookups/dsearch.c exim4-4.86~RC4/src/lookups/dsearch.c --- exim4-4.84/src/lookups/dsearch.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/dsearch.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* The idea for this code came from Matthew Byng-Maddick, but his original has @@ -66,7 +66,7 @@ for us. */ int -static dsearch_find(void *handle, uschar *dirname, uschar *keystring, int length, +static dsearch_find(void *handle, uschar *dirname, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { struct stat statbuf; diff -Nru exim4-4.84/src/lookups/ldap.c exim4-4.86~RC4/src/lookups/ldap.c --- exim4-4.84/src/lookups/ldap.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/ldap.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Many thanks to Stuart Lynne for contributing the original code for this @@ -130,9 +130,10 @@ */ static int -perform_ldap_search(uschar *ldap_url, uschar *server, int s_port, int search_type, - uschar **res, uschar **errmsg, BOOL *defer_break, uschar *user, uschar *password, - int sizelimit, int timelimit, int tcplimit, int dereference, void *referrals) +perform_ldap_search(const uschar *ldap_url, uschar *server, int s_port, + int search_type, uschar **res, uschar **errmsg, BOOL *defer_break, + uschar *user, uschar *password, int sizelimit, int timelimit, int tcplimit, + int dereference, void *referrals) { LDAPURLDesc *ludp = NULL; LDAPMessage *result = NULL; @@ -797,7 +798,13 @@ DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value); - if (values != firstval) + /* In case we requested one attribute only but got + * several times into that attr loop, we need to append + * the additional values. (This may happen if you derive + * attributeTypes B and C from A and then query for A.) + * In all other cases we detect the different attribute + * and append only every non first value. */ + if ((attr_count == 1 && data) || (values != firstval)) data = string_cat(data, &size, &ptr, US",", 1); /* For multiple attributes, the data is in quotes. We must escape @@ -1119,7 +1126,7 @@ */ static int -control_ldap_search(uschar *ldap_url, int search_type, uschar **res, +control_ldap_search(const uschar *ldap_url, int search_type, uschar **res, uschar **errmsg) { BOOL defer_break = FALSE; @@ -1129,12 +1136,13 @@ int sep = 0; int dereference = LDAP_DEREF_NEVER; void* referrals = LDAP_OPT_ON; -uschar *url = ldap_url; -uschar *p; +const uschar *url = ldap_url; +const uschar *p; uschar *user = NULL; uschar *password = NULL; uschar *local_servers = NULL; -uschar *server, *list; +uschar *server; +const uschar *list; uschar buffer[512]; while (isspace(*url)) url++; @@ -1146,7 +1154,7 @@ while (strncmpic(url, US"ldap", 4) != 0) { - uschar *name = url; + const uschar *name = url; while (*url != 0 && *url != '=') url++; if (*url == '=') { @@ -1330,7 +1338,7 @@ The handle and filename arguments are not used. */ static int -eldap_find(void *handle, uschar *filename, uschar *ldap_url, int length, +eldap_find(void *handle, uschar *filename, const uschar *ldap_url, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { /* Keep picky compilers happy */ @@ -1339,7 +1347,7 @@ } static int -eldapm_find(void *handle, uschar *filename, uschar *ldap_url, int length, +eldapm_find(void *handle, uschar *filename, const uschar *ldap_url, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { /* Keep picky compilers happy */ @@ -1348,7 +1356,7 @@ } static int -eldapdn_find(void *handle, uschar *filename, uschar *ldap_url, int length, +eldapdn_find(void *handle, uschar *filename, const uschar *ldap_url, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { /* Keep picky compilers happy */ @@ -1357,7 +1365,7 @@ } int -eldapauth_find(void *handle, uschar *filename, uschar *ldap_url, int length, +eldapauth_find(void *handle, uschar *filename, const uschar *ldap_url, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { /* Keep picky compilers happy */ diff -Nru exim4-4.84/src/lookups/ldap.h exim4-4.86~RC4/src/lookups/ldap.h --- exim4-4.84/src/lookups/ldap.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/ldap.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,12 +2,12 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Header for eldapauth_find */ -extern int eldapauth_find(void *, uschar *, uschar *, int, uschar **, +extern int eldapauth_find(void *, uschar *, const uschar *, int, uschar **, uschar **, BOOL *); /* End of lookups/ldap.h */ diff -Nru exim4-4.84/src/lookups/lf_functions.h exim4-4.86~RC4/src/lookups/lf_functions.h --- exim4-4.84/src/lookups/lf_functions.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/lf_functions.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Header for the functions that are shared by the lookups */ @@ -10,8 +10,9 @@ extern int lf_check_file(int, uschar *, int, int, uid_t *, gid_t *, const char *, uschar **); extern uschar *lf_quote(uschar *, uschar *, int, uschar *, int *, int *); -extern int lf_sqlperform(uschar *, uschar *, uschar *, uschar *, uschar **, - uschar **, BOOL *, int(*)(uschar *, uschar *, uschar **, +extern int lf_sqlperform(const uschar *, const uschar *, const uschar *, + const uschar *, uschar **, + uschar **, BOOL *, int(*)(const uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *)); /* End of lf_functions.h */ diff -Nru exim4-4.84/src/lookups/lf_sqlperform.c exim4-4.86~RC4/src/lookups/lf_sqlperform.c --- exim4-4.84/src/lookups/lf_sqlperform.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/lf_sqlperform.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -34,13 +34,14 @@ */ int -lf_sqlperform(uschar *name, uschar *optionname, uschar *optserverlist, - uschar *query, uschar **result, uschar **errmsg, BOOL *do_cache, - int(*fn)(uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *)) +lf_sqlperform(const uschar *name, const uschar *optionname, + const uschar *optserverlist, const uschar *query, + uschar **result, uschar **errmsg, BOOL *do_cache, + int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *)) { int sep, rc; uschar *server; -uschar *serverlist; +const uschar *serverlist; uschar buffer[512]; BOOL defer_break = FALSE; @@ -68,8 +69,8 @@ else { int qsep; - uschar *s, *ss; - uschar *qserverlist; + const uschar *s, *ss; + const uschar *qserverlist; uschar *qserver; uschar qbuffer[512]; diff -Nru exim4-4.84/src/lookups/lsearch.c exim4-4.86~RC4/src/lookups/lsearch.c --- exim4-4.84/src/lookups/lsearch.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/lsearch.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -71,7 +71,7 @@ but people do occasionally do weird things. */ static int -internal_lsearch_find(void *handle, uschar *filename, uschar *keystring, +internal_lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, int type) { FILE *f = (FILE *)handle; @@ -146,7 +146,7 @@ uschar *t = s++; while (*s != 0 && *s != '\"') { - if (*s == '\\') *t++ = string_interpret_escape(&s); + if (*s == '\\') *t++ = string_interpret_escape(CUSS &s); else *t++ = *s; s++; } @@ -181,7 +181,7 @@ { int rc; int save = buffer[linekeylength]; - uschar *list = buffer; + const uschar *list = buffer; buffer[linekeylength] = 0; rc = match_isinlist(keystring, &list, @@ -322,7 +322,7 @@ /* See local README for interface description */ static int -lsearch_find(void *handle, uschar *filename, uschar *keystring, int length, +lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ @@ -339,7 +339,7 @@ /* See local README for interface description */ static int -wildlsearch_find(void *handle, uschar *filename, uschar *keystring, int length, +wildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ @@ -356,7 +356,7 @@ /* See local README for interface description */ static int -nwildlsearch_find(void *handle, uschar *filename, uschar *keystring, int length, +nwildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ @@ -374,7 +374,7 @@ /* See local README for interface description */ static int -iplsearch_find(void *handle, uschar *filename, uschar *keystring, int length, +iplsearch_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { do_cache = do_cache; /* Keep picky compilers happy */ diff -Nru exim4-4.84/src/lookups/mysql.c exim4-4.86~RC4/src/lookups/mysql.c --- exim4-4.84/src/lookups/mysql.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/mysql.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Thanks to Paul Kelly for contributing the original code for these @@ -84,7 +84,7 @@ */ static int -perform_mysql_search(uschar *query, uschar *server, uschar **resultptr, +perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr, uschar **errmsg, BOOL *defer_break, BOOL *do_cache) { MYSQL *mysql_handle = NULL; /* Keep compilers happy */ @@ -340,7 +340,7 @@ shared with other SQL lookups. */ static int -mysql_find(void *handle, uschar *filename, uschar *query, int length, +mysql_find(void *handle, uschar *filename, const uschar *query, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query, diff -Nru exim4-4.84/src/lookups/passwd.c exim4-4.86~RC4/src/lookups/passwd.c --- exim4-4.84/src/lookups/passwd.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/passwd.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -33,7 +33,7 @@ /* See local README for interface description */ static int -passwd_find(void *handle, uschar *filename, uschar *keystring, int length, +passwd_find(void *handle, uschar *filename, const uschar *keystring, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { struct passwd *pw; diff -Nru exim4-4.84/src/lookups/pgsql.c exim4-4.86~RC4/src/lookups/pgsql.c --- exim4-4.84/src/lookups/pgsql.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/pgsql.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Thanks to Petr Cech for contributing the original code for these @@ -118,7 +118,7 @@ */ static int -perform_pgsql_search(uschar *query, uschar *server, uschar **resultptr, +perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr, uschar **errmsg, BOOL *defer_break, BOOL *do_cache) { PGconn *pg_conn = NULL; @@ -398,7 +398,7 @@ shared with other SQL lookups. */ static int -pgsql_find(void *handle, uschar *filename, uschar *query, int length, +pgsql_find(void *handle, uschar *filename, const uschar *query, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query, diff -Nru exim4-4.84/src/lookups/redis.c exim4-4.86~RC4/src/lookups/redis.c --- exim4-4.84/src/lookups/redis.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/redis.c 2015-06-27 17:01:28.000000000 +0200 @@ -211,7 +211,7 @@ break; case REDIS_REPLY_INTEGER: - ttmp = (redis_reply->integer == 1) ? US"true" : US"false"; + ttmp = (redis_reply->integer != 0) ? US"true" : US"false"; result = string_cat(result, &ssize, &offset, US ttmp, Ustrlen(ttmp)); break; case REDIS_REPLY_STRING: diff -Nru exim4-4.84/src/lookups/sqlite.c exim4-4.86~RC4/src/lookups/sqlite.c --- exim4-4.84/src/lookups/sqlite.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/sqlite.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -80,7 +80,7 @@ static int -sqlite_find(void *handle, uschar *filename, uschar *query, int length, +sqlite_find(void *handle, uschar *filename, const uschar *query, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { int ret; diff -Nru exim4-4.84/src/lookups/testdb.c exim4-4.86~RC4/src/lookups/testdb.c --- exim4-4.84/src/lookups/testdb.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lookups/testdb.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -37,7 +37,7 @@ /* See local README for interface description. */ static int -testdb_find(void *handle, uschar *filename, uschar *query, int length, +testdb_find(void *handle, uschar *filename, const uschar *query, int length, uschar **result, uschar **errmsg, BOOL *do_cache) { handle = handle; /* Keep picky compilers happy */ diff -Nru exim4-4.84/src/lss.c exim4-4.86~RC4/src/lss.c --- exim4-4.84/src/lss.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/lss.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Support functions for calling from local_scan(). These are mostly just @@ -27,7 +27,7 @@ int lss_match_domain(uschar *domain, uschar *list) { -return match_isinlist(domain, &list, 0, &domainlist_anchor, NULL, MCL_DOMAIN, +return match_isinlist(CUS domain, CUSS &list, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); } @@ -49,7 +49,7 @@ int lss_match_local_part(uschar *local_part, uschar *list, BOOL caseless) { -return match_isinlist(local_part, &list, 0, &localpartlist_anchor, NULL, +return match_isinlist(CUS local_part, CUSS &list, 0, &localpartlist_anchor, NULL, MCL_LOCALPART, caseless, NULL); } @@ -71,7 +71,7 @@ int lss_match_address(uschar *address, uschar *list, BOOL caseless) { -return match_address_list(address, caseless, TRUE, &list, NULL, -1, 0, NULL); +return match_address_list(CUS address, caseless, TRUE, CUSS &list, NULL, -1, 0, NULL); } @@ -95,7 +95,7 @@ int lss_match_host(uschar *host_name, uschar *host_address, uschar *list) { -return verify_check_this_host(&list, NULL, host_name, host_address, NULL); +return verify_check_this_host(CUSS &list, NULL, host_name, host_address, NULL); } diff -Nru exim4-4.84/src/macros.h exim4-4.86~RC4/src/macros.h --- exim4-4.84/src/macros.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/macros.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -12,6 +12,9 @@ #define mac_string(s) # s #define mac_expanded_string(s) mac_string(s) +/* Number of elements of an array */ +#define nelem(arr) (sizeof(arr) / sizeof(*arr)) + /* When running in the test harness, the load average is fudged. */ @@ -106,6 +109,13 @@ #define DEBUG(x) if ((debug_selector & (x)) != 0) #define HDEBUG(x) if (host_checking || (debug_selector & (x)) != 0) +#define PTR_CHK(ptr) \ +do { \ +if ((void *)ptr > (void *)store_get(0)) \ + debug_printf("BUG: ptr '%s' beyond arena at %s:%d\n", \ + mac_expanded_string(ptr), __FUNCTION__, __LINE__); \ +} while(0) + /* The default From: text for DSNs */ #define DEFAULT_DSN_FROM "Mail Delivery System " @@ -149,13 +159,17 @@ as long as the maximum path length. */ #if defined PATH_MAX && PATH_MAX > 16384 -#define BIG_BUFFER_SIZE PATH_MAX +# define BIG_BUFFER_SIZE PATH_MAX #elif defined MAXPATHLEN && MAXPATHLEN > 16384 -#define BIG_BUFFER_SIZE MAXPATHLEN +# define BIG_BUFFER_SIZE MAXPATHLEN #else -#define BIG_BUFFER_SIZE 16384 +# define BIG_BUFFER_SIZE 16384 #endif +/* header size of pipe content + currently: char id, char subid, char[5] length */ +#define PIPE_HEADER_SIZE 7 + /* This limits the length of data returned by local_scan(). Because it is written on the spool, it gets read into big_buffer. */ @@ -439,6 +453,7 @@ LX_rejected_header | \ LX_sender_verify_fail | \ LX_smtp_confirmation | \ + LX_tls_certificate_verified| \ LX_tls_cipher) & 0x7fffffff) /* Private error numbers for delivery failures, set negative so as not @@ -475,7 +490,7 @@ #define ERRNO_UIDFAIL (-29) /* Failed to get uid */ #define ERRNO_BADTRANSPORT (-30) /* Unset or non-existent transport */ #define ERRNO_MBXLENGTH (-31) /* MBX length mismatch */ -#define ERRNO_UNKNOWNHOST (-32) /* Lookup failed in smtp transport */ +#define ERRNO_UNKNOWNHOST (-32) /* Lookup failed routing or in smtp tpt */ #define ERRNO_FORMATUNKNOWN (-33) /* Can't match format in appendfile */ #define ERRNO_BADCREATE (-34) /* Creation outside home in appendfile */ #define ERRNO_LISTDEFER (-35) /* Can't check a list; lookup defer */ @@ -491,6 +506,11 @@ #define ERRNO_MAIL4XX (-45) /* MAIL gave 4xx error */ #define ERRNO_DATA4XX (-46) /* DATA gave 4xx error */ #define ERRNO_PROXYFAIL (-47) /* Negotiation failed for proxy configured host */ +#define ERRNO_AUTHPROB (-48) /* Authenticator "other" failure */ + +#ifdef EXPERIMENTAL_INTERNATIONAL +# define ERRNO_UTF8_FWD (-49) /* target not supporting SMTPUTF8 */ +#endif /* These must be last, so all retry deferments can easily be identified */ @@ -623,7 +643,7 @@ enum { opt_bit = 32, opt_bool_verify, opt_bool_set, opt_expand_bool, opt_bool_last, opt_rewrite, opt_timelist, opt_uid, opt_gid, opt_uidlist, opt_gidlist, - opt_expand_uid, opt_expand_gid, opt_void }; + opt_expand_uid, opt_expand_gid, opt_func, opt_void }; /* There's a high-ish bit which is used to flag duplicate options, kept for compatibility, which shouldn't be output. Also used for hidden options @@ -788,7 +808,6 @@ #define topt_no_body 0x040 /* Omit body */ #define topt_escape_headers 0x080 /* Apply escape check to headers */ -#ifdef EXPERIMENTAL_DSN /* Flags for recipient_block, used in DSN support */ #define rf_dsnlasthop 0x01 /* Do not propagate DSN any further */ @@ -809,7 +828,6 @@ #define dsn_support_yes 1 #define dsn_support_no 2 -#endif /* Codes for the host_find_failed and host_all_ignored options. */ diff -Nru exim4-4.84/src/malware.c exim4-4.86~RC4/src/malware.c --- exim4-4.84/src/malware.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/malware.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner 2003-2014 */ +/* Copyright (c) Tom Kistner 2003 - 2015 */ /* License: GPL */ /* Code for calling virus (malware) scanners. Called from acl.c. */ @@ -11,7 +11,7 @@ #ifdef WITH_CONTENT_SCAN typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL, - M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD} scanner_t; + M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST} scanner_t; typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t; static struct scan { @@ -31,31 +31,26 @@ { M_CLAMD, US"clamd", US"/tmp/clamd", MC_NONE }, { M_SOCK, US"sock", US"/tmp/malware.sock", MC_STRM }, { M_MKSD, US"mksd", NULL, MC_NONE }, + { M_AVAST, US"avast", US"/var/run/avast/scan.sock", MC_STRM }, { -1, NULL, NULL, MC_NONE } /* end-marker */ }; /* The maximum number of clamd servers that are supported in the configuration */ #define MAX_CLAMD_SERVERS 32 #define MAX_CLAMD_SERVERS_S "32" -/* Maximum length of the hostname that can be specified in the clamd address list */ -#define MAX_CLAMD_ADDRESS_LENGTH 64 -#define MAX_CLAMD_ADDRESS_LENGTH_S "64" - -typedef struct clamd_address_container { - uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH+1]; - unsigned int tcp_port; -} clamd_address_container; - -/* declaration of private routines */ -static int mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename); -static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking); + +typedef struct clamd_address { + uschar * hostspec; + unsigned tcp_port; + unsigned retry; +} clamd_address; #ifndef nelements # define nelements(arr) (sizeof(arr) / sizeof(arr[0])) #endif -#define MALWARE_TIMEOUT 120 +#define MALWARE_TIMEOUT 120 /* default timeout, seconds */ #define DRWEBD_SCAN_CMD (1) /* scan file, buffer or diskfile */ @@ -67,6 +62,30 @@ #define DERR_TIMEOUT (1<<9) /* scan timeout has run out */ #define DERR_BAD_CALL (1<<15) /* wrong command */ + +static const uschar * malware_regex_default = US ".+"; +static const pcre * malware_default_re = NULL; + +static const uschar * drweb_re_str = US "infected\\swith\\s*(.+?)$"; +static const pcre * drweb_re = NULL; + +static const uschar * fsec_re_str = US "\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$"; +static const pcre * fsec_re = NULL; + +static const uschar * kav_re_sus_str = US "suspicion:\\s*(.+?)\\s*$"; +static const uschar * kav_re_inf_str = US "infected:\\s*(.+?)\\s*$"; +static const pcre * kav_re_sus = NULL; +static const pcre * kav_re_inf = NULL; + +static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]"; +static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)"; +static const pcre * ava_re_clean = NULL; +static const pcre * ava_re_virus = NULL; + + + +/******************************************************************************/ + /* Routine to check whether a system is big- or little-endian. Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html Needed for proper kavdaemon implementation. Sigh. */ @@ -89,103 +108,26 @@ extern int spool_mbox_ok; extern uschar spooled_message_id[17]; -/************************************************* -* Scan an email for malware * -*************************************************/ - -/* This is the normal interface for scanning an email, which doesn't need a -filename; it's a wrapper around the malware_file function. - -Arguments: - listptr the list of options to the "malware = ..." ACL condition - -Returns: Exim message processing code (OK, FAIL, DEFER, ...) - where true means malware was found (condition applies) -*/ -int -malware(uschar **listptr) -{ - uschar * scan_filename; - int ret; - - scan_filename = string_sprintf("%s/scan/%s/%s.eml", - spool_directory, message_id, message_id); - ret = malware_internal(listptr, scan_filename, FALSE); - if (ret == DEFER) av_failed = TRUE; - - return ret; -} - - -/************************************************* -* Scan a file for malware * -*************************************************/ - -/* This is a test wrapper for scanning an email, which is not used in -normal processing. Scan any file, using the Exim scanning interface. -This function tampers with various global variables so is unsafe to use -in any other context. - -Arguments: - eml_filename a file holding the message to be scanned - -Returns: Exim message processing code (OK, FAIL, DEFER, ...) - where true means malware was found (condition applies) -*/ -int -malware_in_file(uschar *eml_filename) -{ - uschar *scan_options[2]; - uschar message_id_buf[64]; - int ret; - - scan_options[0] = US"*"; - scan_options[1] = NULL; - - /* spool_mbox() assumes various parameters exist, when creating - the relevant directory and the email within */ - (void) string_format(message_id_buf, sizeof(message_id_buf), - "dummy-%d", vaguely_random_number(INT_MAX)); - message_id = message_id_buf; - sender_address = US"malware-sender@example.net"; - return_path = US""; - recipients_list = NULL; - receive_add_recipient(US"malware-victim@example.net", -1); - enable_dollar_recipients = TRUE; - - ret = malware_internal(scan_options, eml_filename, TRUE); - - Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id)); - spool_mbox_ok = 1; - /* don't set no_mbox_unspool; at present, there's no way for it to become - set, but if that changes, then it should apply to these tests too */ - unspool_mbox(); - - /* silence static analysis tools */ - message_id = NULL; - - return ret; -} static inline int malware_errlog_defer(const uschar * str) { - log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str); - return DEFER; +log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str); +return DEFER; } static int m_errlog_defer(struct scan * scanent, const uschar * str) { - return malware_errlog_defer(string_sprintf("%s: %s", scanent->name, str)); +return malware_errlog_defer(string_sprintf("%s: %s", scanent->name, str)); } static int m_errlog_defer_3(struct scan * scanent, const uschar * str, int fd_to_close) { - (void) close(fd_to_close); - return m_errlog_defer(scanent, str); +(void) close(fd_to_close); +return m_errlog_defer(scanent, str); } /*************************************************/ @@ -197,111 +139,259 @@ m_tcpsocket(const uschar * hostname, unsigned int port, host_item * host, uschar ** errstr) { - return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr); +return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr); } static int -m_tcpsocket_fromdef(const uschar * hostport, uschar ** errstr) +m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr) { - int scan; - uschar hostname[256]; - unsigned int portlow, porthigh; - - /* extract host and port part */ - scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh); - if ( scan != 3 ) { - if ( scan != 2 ) { - *errstr = string_sprintf("invalid socket '%s'", hostport); - return -1; - } - porthigh = portlow; +if (send(sock, buf, cnt, 0) < 0) + { + int err = errno; + (void)close(sock); + *errstr = string_sprintf("unable to send to socket (%s): %s", + buf, strerror(err)); + return -1; } +return sock; +} - return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh, - 5, NULL, errstr); +static const pcre * +m_pcre_compile(const uschar * re, uschar ** errstr) +{ +const uschar * rerror; +int roffset; +const pcre * cre; + +cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL); +if (!cre) + *errstr= string_sprintf("regular expression error in '%s': %s at offset %d", + re, rerror, roffset); +return cre; } -static int -m_unixsocket(const uschar * path, uschar ** errstr) +uschar * +m_pcre_exec(const pcre * cre, uschar * text) { - int sock; - struct sockaddr_un server; +int ovector[10*3]; +int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0, + ovector, nelements(ovector)); +uschar * substr = NULL; +if (i >= 2) /* Got it */ + pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr); +return substr; +} - if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - *errstr = US"can't open UNIX socket."; - return -1; +static const pcre * +m_pcre_nextinlist(const uschar ** list, int * sep, + char * listerr, uschar ** errstr) +{ +const uschar * list_ele; +const pcre * cre = NULL; + +if (!(list_ele = string_nextinlist(list, sep, NULL, 0))) + *errstr = US listerr; +else + cre = m_pcre_compile(CUS list_ele, errstr); +return cre; +} + +/* + Simple though inefficient wrapper for reading a line. Drop CRs and the + trailing newline. Can return early on buffer full. Null-terminate. + Apply initial timeout if no data ready. + + Return: number of chars - zero for an empty line + -1 on EOF + -2 on timeout or error +*/ +static int +recv_line(int fd, uschar * buffer, int bsize, int tmo) +{ +uschar * p = buffer; +ssize_t rcv; +BOOL ok = FALSE; + +if (!fd_ready(fd, tmo-time(NULL))) + return -2; + +/*XXX tmo handling assumes we always get a whole line */ +/* read until \n */ +errno = 0; +while ((rcv = read(fd, p, 1)) > 0) + { + ok = TRUE; + if (p-buffer > bsize-2) break; + if (*p == '\n') break; + if (*p != '\r') p++; } +if (!ok) + { + DEBUG(D_acl) debug_printf("Malware scan: read %s (%s)\n", + rcv==0 ? "EOF" : "error", strerror(errno)); + return rcv==0 ? -1 : -2; + } +*p = '\0'; - server.sun_family = AF_UNIX; - Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1); - server.sun_path[sizeof(server.sun_path)-1] = '\0'; - if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) { - int err = errno; - (void)close(sock); - *errstr = string_sprintf("unable to connect to UNIX socket (%s): %s", - path, strerror(err)); - return -1; - } - return sock; +DEBUG(D_acl) debug_printf("Malware scan: read '%s'\n", buffer); +return p - buffer; +} + +/* return TRUE iff size as requested */ +static BOOL +recv_len(int sock, void * buf, int size, int tmo) +{ +return fd_ready(sock, tmo-time(NULL)) + ? recv(sock, buf, size, 0) == size + : FALSE; } + + +/* ============= private routines for the "mksd" scanner type ============== */ + +#include + static inline int -m_streamsocket(const uschar * spec, uschar ** errstr) +mksd_writev (int sock, struct iovec * iov, int iovcnt) { - return *spec == '/' - ? m_unixsocket(spec, errstr) : m_tcpsocket_fromdef(spec, errstr); +int i; + +for (;;) + { + do + i = writev (sock, iov, iovcnt); + while (i < 0 && errno == EINTR); + if (i <= 0) + { + (void) malware_errlog_defer( + US"unable to write to mksd UNIX socket (/var/run/mksd/socket)"); + return -1; + } + for (;;) /* check for short write */ + if (i >= iov->iov_len) + { + if (--iovcnt == 0) + return 0; + i -= iov->iov_len; + iov++; + } + else + { + iov->iov_len -= i; + iov->iov_base = CS iov->iov_base + i; + break; + } + } } -static int -m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr) +static inline int +mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size, int tmo) { - if (send(sock, buf, cnt, 0) < 0) { - int err = errno; - (void)close(sock); - *errstr = string_sprintf("unable to send to socket (%s): %s", - buf, strerror(err)); +int offset = 0; +int i; + +do + { + i = ip_recv(sock, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL)); + if (i <= 0) + { + (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); return -1; } - return sock; + + offset += i; + /* offset == av_buffer_size -> buffer full */ + if (offset == av_buffer_size) + { + (void) malware_errlog_defer(US"malformed reply received from mksd"); + return -1; + } + } while (av_buffer[offset-1] != '\n'); + +av_buffer[offset] = '\0'; +return offset; } -static const pcre * -m_pcre_compile(const uschar * re, uschar ** errstr) +static inline int +mksd_parse_line(struct scan * scanent, char * line) { - const uschar * rerror; - int roffset; - const pcre * cre; - - cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL); - if (!cre) - *errstr= string_sprintf("regular expression error in '%s': %s at offset %d", - re, rerror, roffset); - return cre; +char *p; + +switch (*line) + { + case 'O': /* OK */ + return OK; + + case 'E': + case 'A': /* ERR */ + if ((p = strchr (line, '\n')) != NULL) + *p = '\0'; + return m_errlog_defer(scanent, + string_sprintf("scanner failed: %s", line)); + + default: /* VIR */ + if ((p = strchr (line, '\n')) != NULL) + { + *p = '\0'; + if ( p-line > 5 + && line[3] == ' ' + && (p = strchr(line+4, ' ')) != NULL + && p-line > 4 + ) + { + *p = '\0'; + malware_name = string_copy(US line+4); + return OK; + } + } + return m_errlog_defer(scanent, + string_sprintf("malformed reply received: %s", line)); + } } -uschar * -m_pcre_exec(const pcre * cre, uschar * text) +static int +mksd_scan_packed(struct scan * scanent, int sock, const uschar * scan_filename, + int tmo) { - int ovector[10*3]; - int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0, - ovector, nelements(ovector)); - uschar * substr = NULL; - if (i >= 2) /* Got it */ - pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr); - return substr; +struct iovec iov[3]; +const char *cmd = "MSQ\n"; +uschar av_buffer[1024]; + +iov[0].iov_base = (void *) cmd; +iov[0].iov_len = 3; +iov[1].iov_base = (void *) scan_filename; +iov[1].iov_len = Ustrlen(scan_filename); +iov[2].iov_base = (void *) (cmd + 3); +iov[2].iov_len = 1; + +if (mksd_writev (sock, iov, 3) < 0) + return DEFER; + +if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer), tmo) < 0) + return DEFER; + +return mksd_parse_line (scanent, CS av_buffer); } -static const pcre * -m_pcre_nextinlist(uschar ** list, int * sep, char * listerr, uschar ** errstr) + +static int +clamd_option(clamd_address * cd, const uschar * optstr, int * subsep) { - const uschar * list_ele; - const pcre * cre = NULL; +uschar * s; - if (!(list_ele = string_nextinlist(list, sep, NULL, 0))) - *errstr = US listerr; +cd->retry = 0; +while ((s = string_nextinlist(&optstr, subsep, NULL, 0))) + if (Ustrncmp(s, "retry=", 6) == 0) + { + int sec = readconf_readtime((s += 6), '\0', FALSE); + if (sec < 0) + return FAIL; + cd->retry = sec; + } else - cre = m_pcre_compile(CUS list_ele, errstr); - return cre; + return FAIL; +return OK; } /************************************************* @@ -312,1365 +402,1617 @@ is via malware(), or there's malware_in_file() used for testing/debugging. Arguments: - listptr the list of options to the "malware = ..." ACL condition + malware_re match condition for "malware=" eml_filename the file holding the email to be scanned + timeout if nonzero, non-default timeoutl faking whether or not we're faking this up for the -bmalware test Returns: Exim message processing code (OK, FAIL, DEFER, ...) where true means malware was found (condition applies) */ static int -malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) +malware_internal(const uschar * malware_re, const uschar * eml_filename, + int timeout, BOOL faking) { - int sep = 0; - uschar *list = *listptr; - uschar *av_scanner_work = av_scanner; - uschar *scanner_name; - uschar *malware_regex; - uschar malware_regex_default[] = ".+"; - unsigned long mbox_size; - FILE *mbox_file; - const pcre *re; - uschar * errstr; - struct scan * scanent; - const uschar * scanner_options; - int sock = -1; - - /* make sure the eml mbox file is spooled up */ - if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL))) - return malware_errlog_defer(US"error while creating mbox spool file"); - - /* none of our current scanners need the mbox - file as a stream, so we can close it right away */ - (void)fclose(mbox_file); - - /* extract the malware regex to match against from the option list */ - if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0))) - return FAIL; /* empty means "don't match anything" */ - - /* parse 1st option */ - if ( (strcmpic(malware_regex,US"false") == 0) || - (Ustrcmp(malware_regex,"0") == 0) ) - return FAIL; /* explicitly no matching */ - - /* special cases (match anything except empty) */ - if ( (strcmpic(malware_regex,US"true") == 0) || - (Ustrcmp(malware_regex,"*") == 0) || - (Ustrcmp(malware_regex,"1") == 0) ) - malware_regex = malware_regex_default; - - /* Reset sep that is set by previous string_nextinlist() call */ - sep = 0; - - /* compile the regex, see if it works */ - if (!(re = m_pcre_compile(malware_regex, &errstr))) +int sep = 0; +const uschar *av_scanner_work = av_scanner; +uschar *scanner_name; +unsigned long mbox_size; +FILE *mbox_file; +const pcre *re; +uschar * errstr; +struct scan * scanent; +const uschar * scanner_options; +int sock = -1; +time_t tmo; + +/* make sure the eml mbox file is spooled up */ +if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL))) + return malware_errlog_defer(US"error while creating mbox spool file"); + +/* none of our current scanners need the mbox + file as a stream, so we can close it right away */ +(void)fclose(mbox_file); + +if (!malware_re) + return FAIL; /* empty means "don't match anything" */ + +/* parse 1st option */ + if ( (strcmpic(malware_re, US"false") == 0) || + (Ustrcmp(malware_re,"0") == 0) ) + return FAIL; /* explicitly no matching */ + +/* special cases (match anything except empty) */ +if ( strcmpic(malware_re,US"true") == 0 + || Ustrcmp(malware_re,"*") == 0 + || Ustrcmp(malware_re,"1") == 0 + ) + { + if ( !malware_default_re + && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr))) return malware_errlog_defer(errstr); + malware_re = malware_regex_default; + re = malware_default_re; + } - /* if av_scanner starts with a dollar, expand it first */ - if (*av_scanner == '$') { - if (!(av_scanner_work = expand_string(av_scanner))) - return malware_errlog_defer( - string_sprintf("av_scanner starts with $, but expansion failed: %s", - expand_string_message)); +/* compile the regex, see if it works */ +else if (!(re = m_pcre_compile(malware_re, &errstr))) + return malware_errlog_defer(errstr); + +/* Reset sep that is set by previous string_nextinlist() call */ +sep = 0; + +/* if av_scanner starts with a dollar, expand it first */ +if (*av_scanner == '$') + { + if (!(av_scanner_work = expand_string(av_scanner))) + return malware_errlog_defer( + string_sprintf("av_scanner starts with $, but expansion failed: %s", + expand_string_message)); + DEBUG(D_acl) debug_printf("Expanded av_scanner global: %s\n", av_scanner_work); - /* disable result caching in this case */ - malware_name = NULL; - malware_ok = FALSE; + /* disable result caching in this case */ + malware_name = NULL; + malware_ok = FALSE; } - /* Do not scan twice (unless av_scanner is dynamic). */ - if (!malware_ok) { - - /* find the scanner type from the av_scanner option */ - if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0))) - return malware_errlog_defer(US"av_scanner configuration variable is empty"); - - for (scanent = m_scans; ; scanent++) { - if (!scanent->name) - return malware_errlog_defer(string_sprintf("unknown scanner type '%s'", - scanner_name)); - if (strcmpic(scanner_name, US scanent->name) != 0) - continue; - if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0))) - scanner_options = scanent->options_default; - if (scanent->conn == MC_NONE) - break; - switch(scanent->conn) - { - case MC_TCP: sock = m_tcpsocket_fromdef(scanner_options, &errstr); break; - case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr); break; - case MC_STRM: sock = m_streamsocket(scanner_options, &errstr); break; - default: /* compiler quietening */ break; - } - if (sock < 0) - return m_errlog_defer(scanent, errstr); +/* Do not scan twice (unless av_scanner is dynamic). */ +if (!malware_ok) + { + /* find the scanner type from the av_scanner option */ + if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0))) + return malware_errlog_defer(US"av_scanner configuration variable is empty"); + if (!timeout) timeout = MALWARE_TIMEOUT; + tmo = time(NULL) + timeout; + + for (scanent = m_scans; ; scanent++) + { + if (!scanent->name) + return malware_errlog_defer(string_sprintf("unknown scanner type '%s'", + scanner_name)); + if (strcmpic(scanner_name, US scanent->name) != 0) + continue; + if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0))) + scanner_options = scanent->options_default; + if (scanent->conn == MC_NONE) break; + switch(scanent->conn) + { + case MC_TCP: sock = ip_tcpsocket(scanner_options, &errstr, 5); break; + case MC_UNIX: sock = ip_unixsocket(scanner_options, &errstr); break; + case MC_STRM: sock = ip_streamsocket(scanner_options, &errstr, 5); break; + default: /* compiler quietening */ break; } - DEBUG(D_lookup) debug_printf("Malware scan: %s\n", scanner_name); + if (sock < 0) + return m_errlog_defer(scanent, errstr); + break; + } + DEBUG(D_acl) debug_printf("Malware scan: %s tmo %s\n", scanner_name, readconf_printtime(timeout)); - switch (scanent->scancode) { + switch (scanent->scancode) + { case M_FPROTD: /* "f-protd" scanner type -------------------------------- */ { - uschar *fp_scan_option; - unsigned int detected=0, par_count=0; - uschar * scanrequest; - uschar buf[32768], *strhelper, *strhelper2; - uschar * malware_name_internal = NULL; - - DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name); - scanrequest = string_sprintf("GET %s", eml_filename); - - while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep, - NULL, 0))) { - scanrequest = string_sprintf("%s%s%s", scanrequest, - par_count ? "%20" : "?", fp_scan_option); - par_count++; - } - scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest); + uschar *fp_scan_option; + unsigned int detected=0, par_count=0; + uschar * scanrequest; + uschar buf[32768], *strhelper, *strhelper2; + uschar * malware_name_internal = NULL; + int len; - /* send scan request */ - if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + scanrequest = string_sprintf("GET %s", eml_filename); + + while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep, + NULL, 0))) + { + scanrequest = string_sprintf("%s%s%s", scanrequest, + par_count ? "%20" : "?", fp_scan_option); + par_count++; + } + scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s: %s\n", + scanner_name, scanrequest); - /* We get a lot of empty lines, so we need this hack to check for any data at all */ - while( recv(sock, buf, 1, MSG_PEEK) > 0 ) { - if ( recv_line(sock, buf, sizeof(buf)) > 0) { - if ( Ustrstr(buf, US"")) ) { - if ((strhelper2 = Ustrstr(buf, US"")) != NULL) { - *strhelper2 = '\0'; - malware_name_internal = string_copy(strhelper+6); + /* send scan request */ + if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) + return m_errlog_defer(scanent, errstr); + + while ((len = recv_line(sock, buf, sizeof(buf), tmo)) >= 0) + if (len > 0) + { + if (Ustrstr(buf, US""))) + { + if ((strhelper2 = Ustrstr(buf, US"")) != NULL) + { + *strhelper2 = '\0'; + malware_name_internal = string_copy(strhelper+6); } - } else if ( Ustrstr(buf, US"") - ? malware_name_internal : NULL; + } + else if (Ustrstr(buf, US"") + ? malware_name_internal : NULL; + break; + } } + if (len < -1) + { + (void)close(sock); + return DEFER; } - break; + break; } /* f-protd */ case M_DRWEB: /* "drweb" scanner type ----------------------------------- */ - /* v0.1 - added support for tcp sockets */ - /* v0.0 - initial release -- support for unix sockets */ + /* v0.1 - added support for tcp sockets */ + /* v0.0 - initial release -- support for unix sockets */ { - int result; - off_t fsize; - unsigned int fsize_uint; - uschar * tmpbuf, *drweb_fbuf; - int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd, - drweb_vnum, drweb_slen, drweb_fin = 0x0000; - unsigned long bread; - const pcre *drweb_re; - - /* prepare variables */ - drweb_cmd = htonl(DRWEBD_SCAN_CMD); - drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL); + int result; + off_t fsize; + unsigned int fsize_uint; + uschar * tmpbuf, *drweb_fbuf; + int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd, + drweb_vnum, drweb_slen, drweb_fin = 0x0000; + + /* prepare variables */ + drweb_cmd = htonl(DRWEBD_SCAN_CMD); + drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL); - if (*scanner_options != '/') { - - /* calc file size */ - if ((drweb_fd = open(CS eml_filename, O_RDONLY)) == -1) - return m_errlog_defer_3(scanent, - string_sprintf("can't open spool file %s: %s", - eml_filename, strerror(errno)), - sock); + if (*scanner_options != '/') + { + /* calc file size */ + if ((drweb_fd = open(CCS eml_filename, O_RDONLY)) == -1) + return m_errlog_defer_3(scanent, + string_sprintf("can't open spool file %s: %s", + eml_filename, strerror(errno)), + sock); - if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) { - int err = errno; - (void)close(drweb_fd); - return m_errlog_defer_3(scanent, - string_sprintf("can't seek spool file %s: %s", - eml_filename, strerror(err)), - sock); + if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) + { + int err = errno; + (void)close(drweb_fd); + return m_errlog_defer_3(scanent, + string_sprintf("can't seek spool file %s: %s", + eml_filename, strerror(err)), + sock); } - fsize_uint = (unsigned int) fsize; - if ((off_t)fsize_uint != fsize) { - (void)close(drweb_fd); - return m_errlog_defer_3(scanent, - string_sprintf("seeking spool file %s, size overflow", - eml_filename), - sock); + fsize_uint = (unsigned int) fsize; + if ((off_t)fsize_uint != fsize) + { + (void)close(drweb_fd); + return m_errlog_defer_3(scanent, + string_sprintf("seeking spool file %s, size overflow", + eml_filename), + sock); } - drweb_slen = htonl(fsize); - lseek(drweb_fd, 0, SEEK_SET); + drweb_slen = htonl(fsize); + lseek(drweb_fd, 0, SEEK_SET); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n", - scanner_name, scanner_options); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n", + scanner_name, scanner_options); - /* send scan request */ - if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || - (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) || - (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) || - (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) { - (void)close(drweb_fd); - return m_errlog_defer_3(scanent, - string_sprintf("unable to send commands to socket (%s)", scanner_options), - sock); + /* send scan request */ + if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || + (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) || + (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) || + (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) + { + (void)close(drweb_fd); + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send commands to socket (%s)", scanner_options), + sock); } - if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) { - (void)close(drweb_fd); - return m_errlog_defer_3(scanent, - string_sprintf("unable to allocate memory %u for file (%s)", - fsize_uint, eml_filename), - sock); + if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) + { + (void)close(drweb_fd); + return m_errlog_defer_3(scanent, + string_sprintf("unable to allocate memory %u for file (%s)", + fsize_uint, eml_filename), + sock); } - if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) { - int err = errno; - (void)close(drweb_fd); - free(drweb_fbuf); - return m_errlog_defer_3(scanent, - string_sprintf("can't read spool file %s: %s", - eml_filename, strerror(err)), - sock); - } + if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) + { + int err = errno; (void)close(drweb_fd); + free(drweb_fbuf); + return m_errlog_defer_3(scanent, + string_sprintf("can't read spool file %s: %s", + eml_filename, strerror(err)), + sock); + } + (void)close(drweb_fd); - /* send file body to socket */ - if (send(sock, drweb_fbuf, fsize, 0) < 0) { - free(drweb_fbuf); - return m_errlog_defer_3(scanent, - string_sprintf("unable to send file body to socket (%s)", scanner_options), + /* send file body to socket */ + if (send(sock, drweb_fbuf, fsize, 0) < 0) + { + free(drweb_fbuf); + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send file body to socket (%s)", scanner_options), sock); } - (void)close(drweb_fd); + (void)close(drweb_fd); + } + else + { + drweb_slen = htonl(Ustrlen(eml_filename)); - } else { + DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n", + scanner_name, scanner_options); - drweb_slen = htonl(Ustrlen(eml_filename)); + /* send scan request */ + if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || + (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) || + (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) || + (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) || + (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send commands to socket (%s)", scanner_options), + sock); + } - DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n", - scanner_name, scanner_options); + /* wait for result */ + if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo)) + return m_errlog_defer_3(scanent, + US"unable to read return code", sock); + drweb_rc = ntohl(drweb_rc); + + if (!recv_len(sock, &drweb_vnum, sizeof(drweb_vnum), tmo)) + return m_errlog_defer_3(scanent, + US"unable to read the number of viruses", sock); + drweb_vnum = ntohl(drweb_vnum); - /* send scan request */ - if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || - (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) || - (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) || - (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) || - (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) - return m_errlog_defer_3(scanent, - string_sprintf("unable to send commands to socket (%s)", scanner_options), - sock); - } + /* "virus(es) found" if virus number is > 0 */ + if (drweb_vnum) + { + int i; - /* wait for result */ - if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) - return m_errlog_defer_3(scanent, - US"unable to read return code", sock); - drweb_rc = ntohl(drweb_rc); + /* setup default virus name */ + malware_name = US"unknown"; - if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) - return m_errlog_defer_3(scanent, - US"unable to read the number of viruses", sock); - drweb_vnum = ntohl(drweb_vnum); + /* set up match regex */ + if (!drweb_re) + drweb_re = m_pcre_compile(drweb_re_str, &errstr); + + /* read and concatenate virus names into one string */ + for (i = 0; i < drweb_vnum; i++) + { + int size = 0, off = 0, ovector[10*3]; + /* read the size of report */ + if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo)) + return m_errlog_defer_3(scanent, + US"cannot read report size", sock); + drweb_slen = ntohl(drweb_slen); + tmpbuf = store_get(drweb_slen); + + /* read report body */ + if (!recv_len(sock, tmpbuf, drweb_slen, tmo)) + return m_errlog_defer_3(scanent, + US"cannot read report string", sock); + tmpbuf[drweb_slen] = '\0'; + + /* try matcher on the line, grab substring */ + result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, + ovector, nelements(ovector)); + if (result >= 2) + { + const char * pre_malware_nb; + + pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb); + + if (i==0) /* the first name we just copy to malware_name */ + malware_name = string_append(NULL, &size, &off, + 1, pre_malware_nb); - /* "virus(es) found" if virus number is > 0 */ - if (drweb_vnum) { - int i; - - /* setup default virus name */ - malware_name = US"unknown"; - - /* set up match regex */ - drweb_re = m_pcre_compile(US"infected\\swith\\s*(.+?)$", &errstr); - - /* read and concatenate virus names into one string */ - for (i=0;i= 2) { - const char * pre_malware_nb; - - pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb); - - if (i==0) /* the first name we just copy to malware_name */ - malware_name = string_append(NULL, &size, &off, - 1, pre_malware_nb); - - else /* concatenate each new virus name to previous */ - malware_name = string_append(malware_name, &size, &off, - 2, "/", pre_malware_nb); + else /* concatenate each new virus name to previous */ + malware_name = string_append(malware_name, &size, &off, + 2, "/", pre_malware_nb); - pcre_free_substring(pre_malware_nb); + pcre_free_substring(pre_malware_nb); } } } - else { - const char *drweb_s = NULL; + else + { + const char *drweb_s = NULL; - if (drweb_rc & DERR_READ_ERR) drweb_s = "read error"; - if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory"; - if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout"; - if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command"; - /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED. - * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM, - * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR - * and others are ignored */ - if (drweb_s) - return m_errlog_defer_3(scanent, - string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s), - sock); + if (drweb_rc & DERR_READ_ERR) drweb_s = "read error"; + if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory"; + if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout"; + if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command"; + /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED. + * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM, + * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR + * and others are ignored */ + if (drweb_s) + return m_errlog_defer_3(scanent, + string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s), + sock); - /* no virus found */ - malware_name = NULL; + /* no virus found */ + malware_name = NULL; } - break; + break; } /* drweb */ case M_AVES: /* "aveserver" scanner type -------------------------------- */ { - uschar buf[32768]; - int result; + uschar buf[32768]; + int result; - /* read aveserver's greeting and see if it is ready (2xx greeting) */ - recv_line(sock, buf, sizeof(buf)); - - if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, - string_sprintf("unavailable (Responded: %s).", - ((buf[0] != 0) ? buf : (uschar *)"nothing") ), - sock); - - /* prepare our command */ - (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n", - eml_filename); - - DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name); + /* read aveserver's greeting and see if it is ready (2xx greeting) */ + buf[0] = 0; + recv_line(sock, buf, sizeof(buf), tmo); + + if (buf[0] != '2') /* aveserver is having problems */ + return m_errlog_defer_3(scanent, + string_sprintf("unavailable (Responded: %s).", + ((buf[0] != 0) ? buf : (uschar *)"nothing") ), + sock); - /* and send it */ - if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + /* prepare our command */ + (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n", + eml_filename); + + /* and send it */ + DEBUG(D_acl) debug_printf("Malware scan: issuing %s %s\n", + scanner_name, buf); + if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0) + return m_errlog_defer(scanent, errstr); - malware_name = NULL; - result = 0; - /* read response lines, find malware name and final response */ - while (recv_line(sock, buf, sizeof(buf)) > 0) { - debug_printf("aveserver: %s\n", buf); - if (buf[0] == '2') - break; - if (buf[0] == '5') { /* aveserver is having problems */ - result = m_errlog_defer(scanent, - string_sprintf("unable to scan file %s (Responded: %s).", - eml_filename, buf)); - break; - } else if (Ustrncmp(buf,"322",3) == 0) { - uschar *p = Ustrchr(&buf[4],' '); - *p = '\0'; - malware_name = string_copy(&buf[4]); + malware_name = NULL; + result = 0; + /* read response lines, find malware name and final response */ + while (recv_line(sock, buf, sizeof(buf), tmo) > 0) + { + if (buf[0] == '2') + break; + if (buf[0] == '5') /* aveserver is having problems */ + { + result = m_errlog_defer(scanent, + string_sprintf("unable to scan file %s (Responded: %s).", + eml_filename, buf)); + break; + } + if (Ustrncmp(buf,"322",3) == 0) + { + uschar *p = Ustrchr(&buf[4], ' '); + *p = '\0'; + malware_name = string_copy(&buf[4]); } } - /* and send it */ - if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0) - return m_errlog_defer(scanent, errstr); - - /* read aveserver's greeting and see if it is ready (2xx greeting) */ - recv_line(sock, buf, sizeof(buf)); + if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0) + return m_errlog_defer(scanent, errstr); - if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, - string_sprintf("unable to quit dialogue (Responded: %s).", - ((buf[0] != 0) ? buf : (uschar *)"nothing") ), - sock); + /* read aveserver's greeting and see if it is ready (2xx greeting) */ + buf[0] = 0; + recv_line(sock, buf, sizeof(buf), tmo); + + if (buf[0] != '2') /* aveserver is having problems */ + return m_errlog_defer_3(scanent, + string_sprintf("unable to quit dialogue (Responded: %s).", + ((buf[0] != 0) ? buf : (uschar *)"nothing") ), + sock); - if (result == DEFER) { - (void)close(sock); - return DEFER; + if (result == DEFER) + { + (void)close(sock); + return DEFER; } - break; + break; } /* aveserver */ case M_FSEC: /* "fsecure" scanner type ---------------------------------- */ { - int i, j, bread = 0; - uschar * file_name; - uschar av_buffer[1024]; - const pcre * fs_inf; - static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n", - US"CONFIGURE\tTIMEOUT\t0\n", - US"CONFIGURE\tMAXARCH\t5\n", - US"CONFIGURE\tMIME\t1\n" }; + int i, j, bread = 0; + uschar * file_name; + uschar av_buffer[1024]; + static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n", + US"CONFIGURE\tTIMEOUT\t0\n", + US"CONFIGURE\tMAXARCH\t5\n", + US"CONFIGURE\tMIME\t1\n" }; + + malware_name = NULL; + + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, scanner_options); + /* pass options */ + memset(av_buffer, 0, sizeof(av_buffer)); + for (i = 0; i != nelements(cmdopt); i++) + { - malware_name = NULL; + if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) + return m_errlog_defer(scanent, errstr); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", - scanner_name, scanner_options); + bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + if (bread > 0) av_buffer[bread]='\0'; + if (bread < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read answer %d (%s)", i, strerror(errno)), + sock); + for (j = 0; j < bread; j++) + if (av_buffer[j] == '\r' || av_buffer[j] == '\n') + av_buffer[j] ='@'; + } - /* pass options */ - memset(av_buffer, 0, sizeof(av_buffer)); - for (i=0; i != nelements(cmdopt); i++) { + /* pass the mailfile to fsecure */ + file_name = string_sprintf("SCAN\t%s\n", eml_filename); - if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) - return m_errlog_defer(scanent, errstr); + if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0) + return m_errlog_defer(scanent, errstr); + + /* set up match */ + /* todo also SUSPICION\t */ + if (!fsec_re) + fsec_re = m_pcre_compile(fsec_re_str, &errstr); + + /* read report, linewise. Apply a timeout as the Fsecure daemon + sometimes wants an answer to "PING" but they won't tell us what */ + { + uschar * p = av_buffer; + uschar * q; - bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT); - if (bread >0) av_buffer[bread]='\0'; - if (bread < 0) + for (;;) + { + errno = ETIMEDOUT; + i = av_buffer+sizeof(av_buffer)-p; + if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0) return m_errlog_defer_3(scanent, - string_sprintf("unable to read answer %d (%s)", i, strerror(errno)), + string_sprintf("unable to read result (%s)", strerror(errno)), sock); - for (j=0;j%s:%s", CS tmpbuf, eml_filename); - p = Ustrrchr(scanrequest, '/'); - if (p) - *p = '\0'; + time_t t; + uschar tmpbuf[1024]; + uschar * scanrequest; + int kav_rc; + unsigned long kav_reportlen, bread; + const pcre *kav_re; + uschar *p; + + /* get current date and time, build scan request */ + time(&t); + /* pdp note: before the eml_filename parameter, this scanned the + directory; not finding documentation, so we'll strip off the directory. + The side-effect is that the test framework scanning may end up in + scanning more than was requested, but for the normal interface, this is + fine. */ + + strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t)); + scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename); + p = Ustrrchr(scanrequest, '/'); + if (p) + *p = '\0'; - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", - scanner_name, scanner_options); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, scanner_options); - /* send scan request */ - if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + /* send scan request */ + if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) + return m_errlog_defer(scanent, errstr); - /* wait for result */ - if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) - return m_errlog_defer_3(scanent, - US"unable to read 2 bytes from socket.", sock); + /* wait for result */ + if (!recv_len(sock, tmpbuf, 2, tmo)) + return m_errlog_defer_3(scanent, + US"unable to read 2 bytes from socket.", sock); + + /* get errorcode from one nibble */ + kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F; + switch(kav_rc) + { + case 5: case 6: /* improper kavdaemon configuration */ + return m_errlog_defer_3(scanent, + US"please reconfigure kavdaemon to NOT disinfect or remove infected files.", + sock); + case 1: + return m_errlog_defer_3(scanent, + US"reported 'scanning not completed' (code 1).", sock); + case 7: + return m_errlog_defer_3(scanent, + US"reported 'kavdaemon damaged' (code 7).", sock); + } - /* get errorcode from one nibble */ - kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F; - switch(kav_rc) + /* code 8 is not handled, since it is ambigous. It appears mostly on + bounces where part of a file has been cut off */ + + /* "virus found" return codes (2-4) */ + if (kav_rc > 1 && kav_rc < 5) { - case 5: case 6: /* improper kavdaemon configuration */ - return m_errlog_defer_3(scanent, - US"please reconfigure kavdaemon to NOT disinfect or remove infected files.", - sock); - case 1: - return m_errlog_defer_3(scanent, - US"reported 'scanning not completed' (code 1).", sock); - case 7: - return m_errlog_defer_3(scanent, - US"reported 'kavdaemon damaged' (code 7).", sock); - } + int report_flag = 0; - /* code 8 is not handled, since it is ambigous. It appears mostly on - bounces where part of a file has been cut off */ + /* setup default virus name */ + malware_name = US"unknown"; - /* "virus found" return codes (2-4) */ - if ((kav_rc > 1) && (kav_rc < 5)) { - int report_flag = 0; - - /* setup default virus name */ - malware_name = US"unknown"; - - report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ]; - - /* read the report, if available */ - if( report_flag == 1 ) { - /* read report size */ - if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) - return m_errlog_defer_3(scanent, - US"cannot read report size", sock); - - /* it's possible that avp returns av_buffer[1] == 1 but the - reportsize is 0 (!?) */ - if (kav_reportlen > 0) { - /* set up match regex, depends on retcode */ - kav_re = m_pcre_compile( kav_rc == 3 - ? US"suspicion:\\s*(.+?)\\s*$" - : US"infected:\\s*(.+?)\\s*$", - &errstr ); - - /* read report, linewise */ - while (kav_reportlen > 0) { - bread = 0; - while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) { - kav_reportlen--; - if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break; - bread++; - } - bread++; - tmpbuf[bread] = '\0'; + report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ]; + + /* read the report, if available */ + if (report_flag == 1) + { + /* read report size */ + if (!recv_len(sock, &kav_reportlen, 4, tmo)) + return m_errlog_defer_3(scanent, + US"cannot read report size", sock); + + /* it's possible that avp returns av_buffer[1] == 1 but the + reportsize is 0 (!?) */ + if (kav_reportlen > 0) + { + /* set up match regex, depends on retcode */ + if (kav_rc == 3) + { + if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr); + kav_re = kav_re_sus; + } + else + { + if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr); + kav_re = kav_re_inf; + } - /* try matcher on the line, grab substring */ - if ((malware_name = m_pcre_exec(kav_re, tmpbuf))) - break; + /* read report, linewise */ + while (kav_reportlen > 0) + { + if ((bread = recv_line(sock, tmpbuf, sizeof(tmpbuf), tmo)) < 0) + break; + kav_reportlen -= bread+1; + + /* try matcher on the line, grab substring */ + if ((malware_name = m_pcre_exec(kav_re, tmpbuf))) + break; } } } } - else /* no virus found */ - malware_name = NULL; + else /* no virus found */ + malware_name = NULL; - break; + break; } case M_CMDL: /* "cmdline" scanner type ---------------------------------- */ { - const uschar *cmdline_scanner = scanner_options; - const pcre *cmdline_trigger_re; - const pcre *cmdline_regex_re; - uschar * file_name; - uschar * commandline; - void (*eximsigchld)(int); - void (*eximsigpipe)(int); - FILE *scanner_out = NULL; - FILE *scanner_record = NULL; - uschar linebuffer[32767]; - int trigger = 0; - uschar *p; - - if (!cmdline_scanner) - return m_errlog_defer(scanent, errstr); + const uschar *cmdline_scanner = scanner_options; + const pcre *cmdline_trigger_re; + const pcre *cmdline_regex_re; + uschar * file_name; + uschar * commandline; + void (*eximsigchld)(int); + void (*eximsigpipe)(int); + FILE *scanner_out = NULL; + int scanner_fd; + FILE *scanner_record = NULL; + uschar linebuffer[32767]; + int rcnt; + int trigger = 0; + uschar *p; - /* find scanner output trigger */ - cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing trigger specification", &errstr); - if (!cmdline_trigger_re) - return m_errlog_defer(scanent, errstr); + if (!cmdline_scanner) + return m_errlog_defer(scanent, errstr); - /* find scanner name regex */ - cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing virus name regex specification", &errstr); - if (!cmdline_regex_re) - return m_errlog_defer(scanent, errstr); + /* find scanner output trigger */ + cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing trigger specification", &errstr); + if (!cmdline_trigger_re) + return m_errlog_defer(scanent, errstr); - /* prepare scanner call; despite the naming, file_name holds a directory - name which is documented as the value given to %s. */ + /* find scanner name regex */ + cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing virus name regex specification", &errstr); + if (!cmdline_regex_re) + return m_errlog_defer(scanent, errstr); - file_name = string_copy(eml_filename); - p = Ustrrchr(file_name, '/'); - if (p) - *p = '\0'; - commandline = string_sprintf(CS cmdline_scanner, file_name); + /* prepare scanner call; despite the naming, file_name holds a directory + name which is documented as the value given to %s. */ - /* redirect STDERR too */ - commandline = string_sprintf("%s 2>&1", commandline); + file_name = string_copy(eml_filename); + p = Ustrrchr(file_name, '/'); + if (p) + *p = '\0'; + commandline = string_sprintf(CS cmdline_scanner, file_name); + + /* redirect STDERR too */ + commandline = string_sprintf("%s 2>&1", commandline); + + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, commandline); + + /* store exims signal handlers */ + eximsigchld = signal(SIGCHLD,SIG_DFL); + eximsigpipe = signal(SIGPIPE,SIG_DFL); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline); + if (!(scanner_out = popen(CS commandline,"r"))) + { + int err = errno; + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + return m_errlog_defer(scanent, + string_sprintf("call (%s) failed: %s.", commandline, strerror(err))); + } + scanner_fd = fileno(scanner_out); - /* store exims signal handlers */ - eximsigchld = signal(SIGCHLD,SIG_DFL); - eximsigpipe = signal(SIGPIPE,SIG_DFL); + file_name = string_sprintf("%s/scan/%s/%s_scanner_output", + spool_directory, message_id, message_id); - if (!(scanner_out = popen(CS commandline,"r"))) { - int err = errno; - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, - string_sprintf("call (%s) failed: %s.", commandline, strerror(err))); + if (!(scanner_record = modefopen(file_name, "wb", SPOOL_MODE))) + { + int err = errno; + (void) pclose(scanner_out); + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + return m_errlog_defer(scanent, string_sprintf( + "opening scanner output file (%s) failed: %s.", + file_name, strerror(err))); } - file_name = string_sprintf("%s/scan/%s/%s_scanner_output", - spool_directory, message_id, message_id); - scanner_record = modefopen(file_name, "wb", SPOOL_MODE); - - if (scanner_record == NULL) { + /* look for trigger while recording output */ + while ((rcnt = recv_line(scanner_fd, linebuffer, + sizeof(linebuffer), tmo))) + { + if (rcnt < 0) + { int err = errno; + if (rcnt == -1) + break; (void) pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, - string_sprintf("opening scanner output file (%s) failed: %s.", - file_name, strerror(err))); - } + return m_errlog_defer(scanent, string_sprintf( + "unable to read from scanner (%s): %s", + commandline, strerror(err))); + } - /* look for trigger while recording output */ - while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out)) { - if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) { - /* short write */ - (void) pclose(scanner_out); - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, - string_sprintf("short write on scanner output file (%s).", file_name)); + if (Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record)) + { + /* short write */ + (void) pclose(scanner_out); + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + return m_errlog_defer(scanent, string_sprintf( + "short write on scanner output file (%s).", file_name)); } - /* try trigger match */ - if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1)) - trigger = 1; + putc('\n', scanner_record); + /* try trigger match */ + if ( !trigger + && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1) + ) + trigger = 1; } - (void)fclose(scanner_record); - sep = pclose(scanner_out); - signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - if (sep != 0) - return m_errlog_defer(scanent, - sep == -1 - ? string_sprintf("running scanner failed: %s", strerror(sep)) - : string_sprintf("scanner returned error code: %d", sep)); - - if (trigger) { - uschar * s; - /* setup default virus name */ - malware_name = US"unknown"; + (void)fclose(scanner_record); + sep = pclose(scanner_out); + signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); + if (sep != 0) + return m_errlog_defer(scanent, + sep == -1 + ? string_sprintf("running scanner failed: %s", strerror(sep)) + : string_sprintf("scanner returned error code: %d", sep)); - /* re-open the scanner output file, look for name match */ - scanner_record = fopen(CS file_name, "rb"); - while(fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) { - /* try match */ - if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) - malware_name = s; + if (trigger) + { + uschar * s; + /* setup default virus name */ + malware_name = US"unknown"; + + /* re-open the scanner output file, look for name match */ + scanner_record = fopen(CS file_name, "rb"); + while (fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) + { + /* try match */ + if ((s = m_pcre_exec(cmdline_regex_re, linebuffer))) + malware_name = s; } - (void)fclose(scanner_record); + (void)fclose(scanner_record); } - else /* no virus found */ - malware_name = NULL; - break; + else /* no virus found */ + malware_name = NULL; + break; } /* cmdline */ case M_SOPHIE: /* "sophie" scanner type --------------------------------- */ { - int bread = 0; - uschar *p; - uschar * file_name; - uschar av_buffer[1024]; - - /* pass the scan directory to sophie */ - file_name = string_copy(eml_filename); - if ((p = Ustrrchr(file_name, '/'))) - *p = '\0'; + int bread = 0; + uschar *p; + uschar * file_name; + uschar av_buffer[1024]; + + /* pass the scan directory to sophie */ + file_name = string_copy(eml_filename); + if ((p = Ustrrchr(file_name, '/'))) + *p = '\0'; + + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", + scanner_name, scanner_options); + + if ( write(sock, file_name, Ustrlen(file_name)) < 0 + || write(sock, "\n", 1) != 1 + ) + return m_errlog_defer_3(scanent, + string_sprintf("unable to write to UNIX socket (%s)", scanner_options), + sock); + + /* wait for result */ + memset(av_buffer, 0, sizeof(av_buffer)); + if ((bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read from UNIX socket (%s)", scanner_options), + sock); + + /* infected ? */ + if (av_buffer[0] == '1') { + uschar * s = Ustrchr(av_buffer, '\n'); + if (s) + *s = '\0'; + malware_name = string_copy(&av_buffer[2]); + } + else if (!strncmp(CS av_buffer, "-1", 2)) + return m_errlog_defer_3(scanent, US"scanner reported error", sock); + else /* all ok, no virus */ + malware_name = NULL; - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", - scanner_name, scanner_options); + break; + } - if ( write(sock, file_name, Ustrlen(file_name)) < 0 - || write(sock, "\n", 1) != 1 - ) - return m_errlog_defer_3(scanent, - string_sprintf("unable to write to UNIX socket (%s)", scanner_options), - sock); + case M_CLAMD: /* "clamd" scanner type ----------------------------------- */ + { +/* This code was originally contributed by David Saez */ +/* There are three scanning methods available to us: +* (1) Use the SCAN command, pointing to a file in the filesystem +* (2) Use the STREAM command, send the data on a separate port +* (3) Use the zINSTREAM command, send the data inline +* The zINSTREAM command was introduced with ClamAV 0.95, which marked +* STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095 +* In Exim, we use SCAN if using a Unix-domain socket or explicitly told that +* the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless +* WITH_OLD_CLAMAV_STREAM is defined. +* See Exim bug 926 for details. */ + + uschar *p, *vname, *result_tag; + int bread=0; + uschar * file_name; + uschar av_buffer[1024]; + uschar *hostname = US""; + host_item connhost; + uschar *clamav_fbuf; + int clam_fd, result; + off_t fsize; + unsigned int fsize_uint; + BOOL use_scan_command = FALSE; + clamd_address * cv[MAX_CLAMD_SERVERS]; + int num_servers = 0; +#ifdef WITH_OLD_CLAMAV_STREAM + unsigned int port; + uschar av_buffer2[1024]; + int sockData; +#else + uint32_t send_size, send_final_zeroblock; +#endif - /* wait for result */ - memset(av_buffer, 0, sizeof(av_buffer)); - if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read from UNIX socket (%s)", scanner_options), - sock); + /*XXX if unixdomain socket, only one server supported. Needs fixing; + there's no reason we should not mix local and remote servers */ - /* infected ? */ - if (av_buffer[0] == '1') { - uschar * s = Ustrchr(av_buffer, '\n'); - if (s) - *s = '\0'; - malware_name = string_copy(&av_buffer[2]); - } - else if (!strncmp(CS av_buffer, "-1", 2)) - return m_errlog_defer_3(scanent, US"scanner reported error", sock); - else /* all ok, no virus */ - malware_name = NULL; + if (*scanner_options == '/') + { + clamd_address * cd; + const uschar * sublist; + int subsep = ' '; + + /* Local file; so we def want to use_scan_command and don't want to try + * passing IP/port combinations */ + use_scan_command = TRUE; + cd = (clamd_address *) store_get(sizeof(clamd_address)); + + /* extract socket-path part */ + sublist = scanner_options; + cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0); - break; - } + /* parse options */ + if (clamd_option(cd, sublist, &subsep) != OK) + return m_errlog_defer(scanent, + string_sprintf("bad option '%s'", scanner_options)); + cv[0] = cd; + } + else + { + /* Go through the rest of the list of host/port and construct an array + * of servers to try. The first one is the bit we just passed from + * scanner_options so process that first and then scan the remainder of + * the address buffer */ + do + { + clamd_address * cd; + const uschar * sublist; + int subsep = ' '; + uschar * s; - case M_CLAMD: /* "clamd" scanner type ----------------------------------- */ - { - /* This code was originally contributed by David Saez */ - /* There are three scanning methods available to us: - * (1) Use the SCAN command, pointing to a file in the filesystem - * (2) Use the STREAM command, send the data on a separate port - * (3) Use the zINSTREAM command, send the data inline - * The zINSTREAM command was introduced with ClamAV 0.95, which marked - * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095 - * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that - * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless - * WITH_OLD_CLAMAV_STREAM is defined. - * See Exim bug 926 for details. */ - - uschar *p, *vname, *result_tag, *response_end; - int bread=0; - uschar * file_name; - uschar av_buffer[1024]; - uschar *hostname = US""; - host_item connhost; - uschar *clamav_fbuf; - int clam_fd, result; - off_t fsize; - unsigned int fsize_uint; - BOOL use_scan_command = FALSE; - clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS]; - int current_server; - int num_servers = 0; - #ifdef WITH_OLD_CLAMAV_STREAM - unsigned int port; - uschar av_buffer2[1024]; - int sockData; - #else - uint32_t send_size, send_final_zeroblock; - #endif - - if (*scanner_options == '/') - /* Local file; so we def want to use_scan_command and don't want to try - * passing IP/port combinations */ - use_scan_command = TRUE; - else { - const uschar *address = scanner_options; - uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20]; - - /* Go through the rest of the list of host/port and construct an array - * of servers to try. The first one is the bit we just passed from - * scanner_options so process that first and then scan the remainder of - * the address buffer */ - do { - clamd_address_container *this_clamd; - - /* The 'local' option means use the SCAN command over the network - * socket (ie common file storage in use) */ - if (strcmpic(address,US"local") == 0) { - use_scan_command = TRUE; - continue; + /* The 'local' option means use the SCAN command over the network + * socket (ie common file storage in use) */ + /*XXX we could accept this also as a local option? */ + if (strcmpic(scanner_options, US"local") == 0) + { + use_scan_command = TRUE; + continue; } - /* XXX: If unsuccessful we should free this memory */ - this_clamd = - (clamd_address_container *)store_get(sizeof(clamd_address_container)); - - /* extract host and port part */ - if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", - this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) { - (void) m_errlog_defer(scanent, - string_sprintf("invalid address '%s'", address)); - continue; - } + cd = (clamd_address *) store_get(sizeof(clamd_address)); - clamd_address_vector[num_servers] = this_clamd; - num_servers++; - if (num_servers >= MAX_CLAMD_SERVERS) { - (void) m_errlog_defer(scanent, - US"More than " MAX_CLAMD_SERVERS_S " clamd servers " - "specified; only using the first " MAX_CLAMD_SERVERS_S ); - break; + /* extract host and port part */ + sublist = scanner_options; + if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0))) + { + (void) m_errlog_defer(scanent, + string_sprintf("missing address: '%s'", scanner_options)); + continue; + } + if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0))) + { + (void) m_errlog_defer(scanent, + string_sprintf("missing port: '%s'", scanner_options)); + continue; } - } while ((address = string_nextinlist(&av_scanner_work, &sep, - address_buffer, - sizeof(address_buffer))) != NULL); + cd->tcp_port = atoi(CS s); - /* check if we have at least one server */ - if (!num_servers) + /* parse options */ + /*XXX should these options be common over scanner types? */ + if (clamd_option(cd, sublist, &subsep) != OK) + { return m_errlog_defer(scanent, - US"no useable server addresses in malware configuration option."); - } + string_sprintf("bad option '%s'", scanner_options)); + continue; + } + + cv[num_servers++] = cd; + if (num_servers >= MAX_CLAMD_SERVERS) + { + (void) m_errlog_defer(scanent, + US"More than " MAX_CLAMD_SERVERS_S " clamd servers " + "specified; only using the first " MAX_CLAMD_SERVERS_S ); + break; + } + } while ((scanner_options = string_nextinlist(&av_scanner_work, &sep, + NULL, 0))); - /* See the discussion of response formats below to see why we really don't - like colons in filenames when passing filenames to ClamAV. */ - if (use_scan_command && Ustrchr(eml_filename, ':')) + /* check if we have at least one server */ + if (!num_servers) return m_errlog_defer(scanent, - string_sprintf("local/SCAN mode incompatible with" \ - " : in path to email filename [%s]", eml_filename)); + US"no useable server addresses in malware configuration option."); + } + + /* See the discussion of response formats below to see why we really + don't like colons in filenames when passing filenames to ClamAV. */ + if (use_scan_command && Ustrchr(eml_filename, ':')) + return m_errlog_defer(scanent, + string_sprintf("local/SCAN mode incompatible with" \ + " : in path to email filename [%s]", eml_filename)); + + /* We have some network servers specified */ + if (num_servers) + { + /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd + * only supports AF_INET, but we should probably be looking to the + * future and rewriting this to be protocol-independent anyway. */ + + while (num_servers > 0) + { + int i = random_number( num_servers ); + clamd_address * cd = cv[i]; - /* We have some network servers specified */ - if (num_servers) { + DEBUG(D_acl) debug_printf("trying server name %s, port %u\n", + cd->hostspec, cd->tcp_port); - /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd - * only supports AF_INET, but we should probably be looking to the - * future and rewriting this to be protocol-independent anyway. */ - - while ( num_servers > 0 ) { - /* Randomly pick a server to start with */ - current_server = random_number( num_servers ); - - debug_printf("trying server name %s, port %u\n", - clamd_address_vector[current_server]->tcp_addr, - clamd_address_vector[current_server]->tcp_port); - - /* Lookup the host. This is to ensure that we connect to the same IP - * on both connections (as one host could resolve to multiple ips) */ - sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr, - clamd_address_vector[current_server]->tcp_port, - &connhost, &errstr); - if (sock >= 0) { + /* Lookup the host. This is to ensure that we connect to the same IP + * on both connections (as one host could resolve to multiple ips) */ + for (;;) + { + sock= m_tcpsocket(cd->hostspec, cd->tcp_port, &connhost, &errstr); + if (sock >= 0) + { /* Connection successfully established with a server */ - hostname = clamd_address_vector[current_server]->tcp_addr; + hostname = cd->hostspec; break; + } + if (cd->retry <= 0) break; + while (cd->retry > 0) cd->retry = sleep(cd->retry); } + if (sock >= 0) + break; - (void) m_errlog_defer(scanent, errstr); + log_write(0, LOG_MAIN, "malware acl condition: %s: %s", + scanent->name, errstr); - /* Remove the server from the list. XXX We should free the memory */ - num_servers--; - int i; - for( i = current_server; i < num_servers; i++ ) - clamd_address_vector[i] = clamd_address_vector[i+1]; + /* Remove the server from the list. XXX We should free the memory */ + num_servers--; + for (; i < num_servers; i++) + cv[i] = cv[i+1]; } - if ( num_servers == 0 ) - return m_errlog_defer(scanent, US"all servers failed"); - - } else { - if ((sock = m_unixsocket(scanner_options, &errstr)) < 0) - return m_errlog_defer(scanent, errstr); + if (num_servers == 0) + return m_errlog_defer(scanent, US"all servers failed"); } + else + for (;;) + { + if ((sock = ip_unixsocket(cv[0]->hostspec, &errstr)) >= 0) + { + hostname = cv[0]->hostspec; + break; + } + if (cv[0]->retry <= 0) + return m_errlog_defer(scanent, errstr); + while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry); + } - /* have socket in variable "sock"; command to use is semi-independent of - * the socket protocol. We use SCAN if is local (either Unix/local - * domain socket, or explicitly told local) else we stream the data. - * How we stream the data depends upon how we were built. */ - - if (!use_scan_command) { - - #ifdef WITH_OLD_CLAMAV_STREAM - /* "STREAM\n" command, get back a "PORT \n" response, send data to - * that port on a second connection; then in the scan-method-neutral - * part, read the response back on the original connection. */ + /* have socket in variable "sock"; command to use is semi-independent of + * the socket protocol. We use SCAN if is local (either Unix/local + * domain socket, or explicitly told local) else we stream the data. + * How we stream the data depends upon how we were built. */ - DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n", - scanner_name); + if (!use_scan_command) + { +#ifdef WITH_OLD_CLAMAV_STREAM + /* "STREAM\n" command, get back a "PORT \n" response, send data to + * that port on a second connection; then in the scan-method-neutral + * part, read the response back on the original connection. */ + + DEBUG(D_acl) debug_printf( + "Malware scan: issuing %s old-style remote scan (PORT)\n", + scanner_name); - /* Pass the string to ClamAV (7 = "STREAM\n") */ - if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0) - return m_errlog_defer(scanent, errstr); + /* Pass the string to ClamAV (7 = "STREAM\n") */ + if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0) + return m_errlog_defer(scanent, errstr); - memset(av_buffer2, 0, sizeof(av_buffer2)); - bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT); + memset(av_buffer2, 0, sizeof(av_buffer2)); + bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), tmo-time(NULL)); - if (bread < 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read PORT from socket (%s)", - strerror(errno)), - sock); + if (bread < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read PORT from socket (%s)", + strerror(errno)), + sock); - if (bread == sizeof(av_buffer2)) - return m_errlog_defer_3(scanent, "buffer too small", sock); + if (bread == sizeof(av_buffer2)) + return m_errlog_defer_3(scanent, "buffer too small", sock); - if (!(*av_buffer2)) - return m_errlog_defer_3(scanent, "ClamAV returned null", sock); + if (!(*av_buffer2)) + return m_errlog_defer_3(scanent, "ClamAV returned null", sock); - av_buffer2[bread] = '\0'; - if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) - return m_errlog_defer_3(scanent, - string_sprintf("Expected port information from clamd, got '%s'", - av_buffer2), - sock); + av_buffer2[bread] = '\0'; + if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) + return m_errlog_defer_3(scanent, + string_sprintf("Expected port information from clamd, got '%s'", + av_buffer2), + sock); - sockData = m_tcpsocket(connhost.address, port, NULL, &errstr); - if (sockData < 0) - return m_errlog_defer_3(scanent, errstr, sock); - - #define CLOSE_SOCKDATA (void)close(sockData) - #else /* WITH_OLD_CLAMAV_STREAM not defined */ - /* New protocol: "zINSTREAM\n" followed by a sequence of - chunks, a 4-byte number (network order), terminated by a zero-length - chunk. */ + sockData = m_tcpsocket(connhost.address, port, NULL, &errstr); + if (sockData < 0) + return m_errlog_defer_3(scanent, errstr, sock); - DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n", - scanner_name); +# define CLOSE_SOCKDATA (void)close(sockData) +#else /* WITH_OLD_CLAMAV_STREAM not defined */ + /* New protocol: "zINSTREAM\n" followed by a sequence of + chunks, a 4-byte number (network order), terminated by a zero-length + chunk. */ + + DEBUG(D_acl) debug_printf( + "Malware scan: issuing %s new-style remote scan (zINSTREAM)\n", + scanner_name); - /* Pass the string to ClamAV (10 = "zINSTREAM\0") */ - if (send(sock, "zINSTREAM", 10, 0) < 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to send zINSTREAM to socket (%s)", - strerror(errno)), - sock); + /* Pass the string to ClamAV (10 = "zINSTREAM\0") */ + if (send(sock, "zINSTREAM", 10, 0) < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to send zINSTREAM to socket (%s)", + strerror(errno)), + sock); - #define CLOSE_SOCKDATA /**/ - #endif +# define CLOSE_SOCKDATA /**/ +#endif - /* calc file size */ - if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) { - int err = errno; - CLOSE_SOCKDATA; - return m_errlog_defer_3(scanent, - string_sprintf("can't open spool file %s: %s", - eml_filename, strerror(err)), - sock); + /* calc file size */ + if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) + { + int err = errno; + CLOSE_SOCKDATA; + return m_errlog_defer_3(scanent, + string_sprintf("can't open spool file %s: %s", + eml_filename, strerror(err)), + sock); } - if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) { - int err = errno; - CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("can't seek spool file %s: %s", - eml_filename, strerror(err)), - sock); + if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) + { + int err = errno; + CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("can't seek spool file %s: %s", + eml_filename, strerror(err)), + sock); } - fsize_uint = (unsigned int) fsize; - if ((off_t)fsize_uint != fsize) { - CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("seeking spool file %s, size overflow", - eml_filename), - sock); + fsize_uint = (unsigned int) fsize; + if ((off_t)fsize_uint != fsize) + { + CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("seeking spool file %s, size overflow", + eml_filename), + sock); } - lseek(clam_fd, 0, SEEK_SET); + lseek(clam_fd, 0, SEEK_SET); - if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) { - CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("unable to allocate memory %u for file (%s)", - fsize_uint, eml_filename), - sock); + if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) + { + CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("unable to allocate memory %u for file (%s)", + fsize_uint, eml_filename), + sock); } - if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) { - int err = errno; - free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd); - return m_errlog_defer_3(scanent, - string_sprintf("can't read spool file %s: %s", - eml_filename, strerror(err)), - sock); + if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) + { + int err = errno; + free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd); + return m_errlog_defer_3(scanent, + string_sprintf("can't read spool file %s: %s", + eml_filename, strerror(err)), + sock); } - (void)close(clam_fd); + (void)close(clam_fd); - /* send file body to socket */ - #ifdef WITH_OLD_CLAMAV_STREAM - if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) { - free(clamav_fbuf); CLOSE_SOCKDATA; - return m_errlog_defer_3(scanent, - string_sprintf("unable to send file body to socket (%s:%u)", - hostname, port), - sock); + /* send file body to socket */ +#ifdef WITH_OLD_CLAMAV_STREAM + if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) + { + free(clamav_fbuf); CLOSE_SOCKDATA; + return m_errlog_defer_3(scanent, + string_sprintf("unable to send file body to socket (%s:%u)", + hostname, port), + sock); } - #else - send_size = htonl(fsize_uint); - send_final_zeroblock = 0; - if ((send(sock, &send_size, sizeof(send_size), 0) < 0) || - (send(sock, clamav_fbuf, fsize_uint, 0) < 0) || - (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) - { - free(clamav_fbuf); - return m_errlog_defer_3(scanent, - string_sprintf("unable to send file body to socket (%s)", hostname), - sock); - } - #endif - +#else + send_size = htonl(fsize_uint); + send_final_zeroblock = 0; + if ((send(sock, &send_size, sizeof(send_size), 0) < 0) || + (send(sock, clamav_fbuf, fsize_uint, 0) < 0) || + (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) + { free(clamav_fbuf); + return m_errlog_defer_3(scanent, + string_sprintf("unable to send file body to socket (%s)", hostname), + sock); + } +#endif - CLOSE_SOCKDATA; - #undef CLOSE_SOCKDATA - - } else { /* use scan command */ - /* Send a SCAN command pointing to a filename; then in the then in the - * scan-method-neutral part, read the response back */ - - /* ================================================================= */ - - /* Prior to the reworking post-Exim-4.72, this scanned a directory, - which dates to when ClamAV needed us to break apart the email into the - MIME parts (eg, with the now deprecated demime condition coming first). - Some time back, ClamAV gained the ability to deconstruct the emails, so - doing this would actually have resulted in the mail attachments being - scanned twice, in the broken out files and from the original .eml. - Since ClamAV now handles emails (and has for quite some time) we can - just use the email file itself. */ - /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */ - file_name = string_sprintf("SCAN %s\n", eml_filename); - - DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n", - scanner_name, scanner_options); - - if (send(sock, file_name, Ustrlen(file_name), 0) < 0) - return m_errlog_defer_3(scanent, - string_sprintf("unable to write to socket (%s)", strerror(errno)), - sock); + free(clamav_fbuf); - /* Do not shut down the socket for writing; a user report noted that - * clamd 0.70 does not react well to this. */ + CLOSE_SOCKDATA; +#undef CLOSE_SOCKDATA } - /* Commands have been sent, no matter which scan method or connection - * type we're using; now just read the result, independent of method. */ + else + { /* use scan command */ + /* Send a SCAN command pointing to a filename; then in the then in the + * scan-method-neutral part, read the response back */ + +/* ================================================================= */ + + /* Prior to the reworking post-Exim-4.72, this scanned a directory, + which dates to when ClamAV needed us to break apart the email into the + MIME parts (eg, with the now deprecated demime condition coming first). + Some time back, ClamAV gained the ability to deconstruct the emails, so + doing this would actually have resulted in the mail attachments being + scanned twice, in the broken out files and from the original .eml. + Since ClamAV now handles emails (and has for quite some time) we can + just use the email file itself. */ + /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */ + file_name = string_sprintf("SCAN %s\n", eml_filename); - /* Read the result */ - memset(av_buffer, 0, sizeof(av_buffer)); - bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT); - (void)close(sock); - sock = -1; - - if (!(bread > 0)) - return m_errlog_defer(scanent, - string_sprintf("unable to read from socket (%s)", strerror(errno))); + DEBUG(D_acl) debug_printf( + "Malware scan: issuing %s local-path scan [%s]\n", + scanner_name, scanner_options); - if (bread == sizeof(av_buffer)) - return m_errlog_defer(scanent, US"buffer too small"); - /* We're now assured of a NULL at the end of av_buffer */ - - /* Check the result. ClamAV returns one of two result formats. - In the basic mode, the response is of the form: - infected: -> ": FOUND" - not-infected: -> ": OK" - error: -> ": ERROR - If the ExtendedDetectionInfo option has been turned on, then we get: - ": (:) FOUND" - for the infected case. Compare: - /tmp/eicar.com: Eicar-Test-Signature FOUND - /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND - - In the streaming case, clamd uses the filename "stream" which you should - be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The - client app will replace "stream" with the original filename before returning - results to stdout, but the trace shows the data). - - We will assume that the pathname passed to clamd from Exim does not contain - a colon. We will have whined loudly above if the eml_filename does (and we're - passing a filename to clamd). */ - - if (!(*av_buffer)) - return m_errlog_defer(scanent, US"ClamAV returned null"); - - /* strip newline at the end (won't be present for zINSTREAM) - (also any trailing whitespace, which shouldn't exist, but we depend upon - this below, so double-check) */ - p = av_buffer + Ustrlen(av_buffer) - 1; - if (*p == '\n') *p = '\0'; + if (send(sock, file_name, Ustrlen(file_name), 0) < 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to write to socket (%s)", strerror(errno)), + sock); - DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer); + /* Do not shut down the socket for writing; a user report noted that + * clamd 0.70 does not react well to this. */ + } + /* Commands have been sent, no matter which scan method or connection + * type we're using; now just read the result, independent of method. */ - while (isspace(*--p) && (p > av_buffer)) - *p = '\0'; - if (*p) ++p; - response_end = p; + /* Read the result */ + memset(av_buffer, 0, sizeof(av_buffer)); + bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); + (void)close(sock); + sock = -1; + + if (bread <= 0) + return m_errlog_defer(scanent, + string_sprintf("unable to read from socket (%s)", + errno == 0 ? "EOF" : strerror(errno))); + + if (bread == sizeof(av_buffer)) + return m_errlog_defer(scanent, US"buffer too small"); + /* We're now assured of a NULL at the end of av_buffer */ + + /* Check the result. ClamAV returns one of two result formats. + In the basic mode, the response is of the form: + infected: -> ": FOUND" + not-infected: -> ": OK" + error: -> ": ERROR + If the ExtendedDetectionInfo option has been turned on, then we get: + ": (:) FOUND" + for the infected case. Compare: +/tmp/eicar.com: Eicar-Test-Signature FOUND +/tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND + + In the streaming case, clamd uses the filename "stream" which you should + be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The + client app will replace "stream" with the original filename before returning + results to stdout, but the trace shows the data). + + We will assume that the pathname passed to clamd from Exim does not contain + a colon. We will have whined loudly above if the eml_filename does (and we're + passing a filename to clamd). */ + + if (!(*av_buffer)) + return m_errlog_defer(scanent, US"ClamAV returned null"); + + /* strip newline at the end (won't be present for zINSTREAM) + (also any trailing whitespace, which shouldn't exist, but we depend upon + this below, so double-check) */ + p = av_buffer + Ustrlen(av_buffer) - 1; + if (*p == '\n') *p = '\0'; + + DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer); + + while (isspace(*--p) && (p > av_buffer)) + *p = '\0'; + if (*p) ++p; + + /* colon in returned output? */ + if(!(p = Ustrchr(av_buffer,':'))) + return m_errlog_defer(scanent, string_sprintf( + "ClamAV returned malformed result (missing colon): %s", + av_buffer)); + + /* strip filename */ + while (*p && isspace(*++p)) /**/; + vname = p; + + /* It would be bad to encounter a virus with "FOUND" in part of the name, + but we should at least be resistant to it. */ + p = Ustrrchr(vname, ' '); + result_tag = p ? p+1 : vname; - /* colon in returned output? */ - if((p = Ustrchr(av_buffer,':')) == NULL) - return m_errlog_defer(scanent, - string_sprintf("ClamAV returned malformed result (missing colon): %s", - av_buffer)); - - /* strip filename */ - while (*p && isspace(*++p)) /**/; - vname = p; - - /* It would be bad to encounter a virus with "FOUND" in part of the name, - but we should at least be resistant to it. */ - p = Ustrrchr(vname, ' '); - result_tag = p ? p+1 : vname; - - if (Ustrcmp(result_tag, "FOUND") == 0) { - /* p should still be the whitespace before the result_tag */ - while (isspace(*p)) --p; - *++p = '\0'; - /* Strip off the extended information too, which will be in parens - after the virus name, with no intervening whitespace. */ - if (*--p == ')') { - /* "(hash:size)", so previous '(' will do; if not found, we have - a curious virus name, but not an error. */ - p = Ustrrchr(vname, '('); - if (p) - *p = '\0'; + if (Ustrcmp(result_tag, "FOUND") == 0) + { + /* p should still be the whitespace before the result_tag */ + while (isspace(*p)) --p; + *++p = '\0'; + /* Strip off the extended information too, which will be in parens + after the virus name, with no intervening whitespace. */ + if (*--p == ')') + { + /* "(hash:size)", so previous '(' will do; if not found, we have + a curious virus name, but not an error. */ + p = Ustrrchr(vname, '('); + if (p) + *p = '\0'; } - malware_name = string_copy(vname); - DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name); + malware_name = string_copy(vname); + DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name); - } else if (Ustrcmp(result_tag, "ERROR") == 0) - return m_errlog_defer(scanent, - string_sprintf("ClamAV returned: %s", av_buffer)); + } + else if (Ustrcmp(result_tag, "ERROR") == 0) + return m_errlog_defer(scanent, + string_sprintf("ClamAV returned: %s", av_buffer)); - else if (Ustrcmp(result_tag, "OK") == 0) { - /* Everything should be OK */ - malware_name = NULL; - DEBUG(D_acl) debug_printf("Malware not found\n"); + else if (Ustrcmp(result_tag, "OK") == 0) + { + /* Everything should be OK */ + malware_name = NULL; + DEBUG(D_acl) debug_printf("Malware not found\n"); - } else - return m_errlog_defer(scanent, - string_sprintf("unparseable response from ClamAV: {%s}", av_buffer)); + } + else + return m_errlog_defer(scanent, + string_sprintf("unparseable response from ClamAV: {%s}", av_buffer)); - break; + break; } /* clamd */ case M_SOCK: /* "sock" scanner type ------------------------------------- */ - /* This code was derived by Martin Poole from the clamd code contributed - by David Saez and the cmdline code - */ + /* This code was derived by Martin Poole from the clamd code contributed + by David Saez and the cmdline code + */ { - int bread; - uschar * commandline; - uschar av_buffer[1024]; - uschar * linebuffer; - uschar * sockline_scanner; - uschar sockline_scanner_default[] = "%s\n"; - const pcre *sockline_trig_re; - const pcre *sockline_name_re; - - /* find scanner command line */ - if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep, - NULL, 0))) - { /* check for no expansions apart from one %s */ - char * s = index(CS sockline_scanner, '%'); - if (s++) - if ((*s != 's' && *s != '%') || index(s+1, '%')) - return m_errlog_defer_3(scanent, - US"unsafe sock scanner call spec", sock); - } - else - sockline_scanner = sockline_scanner_default; - - /* find scanner output trigger */ - sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing trigger specification", &errstr); - if (!sockline_trig_re) - return m_errlog_defer_3(scanent, errstr, sock); - - /* find virus name regex */ - sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, - "missing virus name regex specification", &errstr); - if (!sockline_name_re) - return m_errlog_defer_3(scanent, errstr, sock); - - /* prepare scanner call - security depends on expansions check above */ - commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id); - commandline = string_sprintf( CS sockline_scanner, CS commandline); + int bread; + uschar * commandline; + uschar av_buffer[1024]; + uschar * linebuffer; + uschar * sockline_scanner; + uschar sockline_scanner_default[] = "%s\n"; + const pcre *sockline_trig_re; + const pcre *sockline_name_re; + + /* find scanner command line */ + if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep, + NULL, 0))) + { /* check for no expansions apart from one %s */ + uschar * s = Ustrchr(sockline_scanner, '%'); + if (s++) + if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%')) + return m_errlog_defer_3(scanent, + US"unsafe sock scanner call spec", sock); + } + else + sockline_scanner = sockline_scanner_default; + /* find scanner output trigger */ + sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing trigger specification", &errstr); + if (!sockline_trig_re) + return m_errlog_defer_3(scanent, errstr, sock); + + /* find virus name regex */ + sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, + "missing virus name regex specification", &errstr); + if (!sockline_name_re) + return m_errlog_defer_3(scanent, errstr, sock); + + /* prepare scanner call - security depends on expansions check above */ + commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id); + commandline = string_sprintf( CS sockline_scanner, CS commandline); - /* Pass the command string to the socket */ - if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0) - return m_errlog_defer(scanent, errstr); - /* Read the result */ - memset(av_buffer, 0, sizeof(av_buffer)); - bread = read(sock, av_buffer, sizeof(av_buffer)); + /* Pass the command string to the socket */ + if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0) + return m_errlog_defer(scanent, errstr); - if (!(bread > 0)) - return m_errlog_defer_3(scanent, - string_sprintf("unable to read from socket (%s)", strerror(errno)), - sock); + /* Read the result */ + bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); - if (bread == sizeof(av_buffer)) - return m_errlog_defer_3(scanent, US"buffer too small", sock); - linebuffer = string_copy(av_buffer); + if (bread <= 0) + return m_errlog_defer_3(scanent, + string_sprintf("unable to read from socket (%s)", strerror(errno)), + sock); + + if (bread == sizeof(av_buffer)) + return m_errlog_defer_3(scanent, US"buffer too small", sock); + av_buffer[bread] = '\0'; + linebuffer = string_copy(av_buffer); - /* try trigger match */ - if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) { - if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer))) - malware_name = US "unknown"; + /* try trigger match */ + if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) + { + if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer))) + malware_name = US "unknown"; } - else /* no virus found */ - malware_name = NULL; - break; + else /* no virus found */ + malware_name = NULL; + break; } case M_MKSD: /* "mksd" scanner type ------------------------------------- */ { - char *mksd_options_end; - int mksd_maxproc = 1; /* default, if no option supplied */ - int sock; - int retval; - - if (scanner_options) { - mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10); - if ( *scanner_options == '\0' - || *mksd_options_end != '\0' - || mksd_maxproc < 1 - || mksd_maxproc > 32 - ) - return m_errlog_defer(scanent, - string_sprintf("invalid option '%s'", scanner_options)); + char *mksd_options_end; + int mksd_maxproc = 1; /* default, if no option supplied */ + int retval; + + if (scanner_options) + { + mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10); + if ( *scanner_options == '\0' + || *mksd_options_end != '\0' + || mksd_maxproc < 1 + || mksd_maxproc > 32 + ) + return m_errlog_defer(scanent, + string_sprintf("invalid option '%s'", scanner_options)); } - if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0) - return m_errlog_defer(scanent, errstr); + if((sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0) + return m_errlog_defer(scanent, errstr); - malware_name = NULL; + malware_name = NULL; - DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name); + DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name); - if ((retval = mksd_scan_packed(scanent, sock, eml_filename)) != OK) { - close (sock); - return retval; + if ((retval = mksd_scan_packed(scanent, sock, eml_filename, tmo)) != OK) + { + close (sock); + return retval; } - break; + break; } - } - if (sock >= 0) - (void) close (sock); - malware_ok = TRUE; /* set "been here, done that" marker */ - } + case M_AVAST: /* "avast" scanner type ----------------------------------- */ + { + int ovector[1*3]; + uschar buf[1024]; + uschar * scanrequest; + enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage; + int nread; + + /* According to Martin Tuma @avast the protocol uses "escaped + whitespace", that is, every embedded whitespace is backslash + escaped, as well as backslash is protected by backslash. + The returned lines contain the name of the scanned file, a tab + and the [ ] marker. + [+] - not infected + [L] - infected + [E] - some error occured + Such marker follows the first non-escaped TAB. */ + if ( ( !ava_re_clean + && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr))) + || ( !ava_re_virus + && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr))) + ) + return malware_errlog_defer(errstr); + + /* wait for result */ + for (avast_stage = AVA_HELO; + (nread = recv_line(sock, buf, sizeof(buf), tmo)) > 0; + ) + { + int slen = Ustrlen(buf); + if (slen >= 1) + { + DEBUG(D_acl) debug_printf("got from avast: %s\n", buf); + switch (avast_stage) + { + case AVA_HELO: + if (Ustrncmp(buf, "220", 3) != 0) + goto endloop; /* require a 220 */ + goto sendreq; + + case AVA_OPT: + if (Ustrncmp(buf, "210", 3) == 0) + break; /* ignore 210 responses */ + if (Ustrncmp(buf, "200", 3) != 0) + goto endloop; /* require a 200 */ + + sendreq: + { + int len; + /* Check for another option to send. Newline-terminate it. */ + if ((scanrequest = string_nextinlist(&av_scanner_work, &sep, + NULL, 0))) + { + scanrequest = string_sprintf("%s\n", scanrequest); + avast_stage = AVA_OPT; /* just sent option */ + } + else + { + scanrequest = string_sprintf("SCAN %s/scan/%s\n", + spool_directory, message_id); + avast_stage = AVA_RSP; /* just sent command */ + } - /* match virus name against pattern (caseless ------->----------v) */ - if ( malware_name && (regex_match_and_setup(re, malware_name, 0, -1)) ) { - DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name); - return OK; - } - else - return FAIL; -} + /* send config-cmd or scan-request to socket */ + len = Ustrlen(scanrequest); + if (send(sock, scanrequest, len, 0) < 0) + { + scanrequest[len-1] = '\0'; + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send request '%s' to socket (%s): %s", + scanrequest, scanner_options, strerror(errno)), sock); + } + break; + } + case AVA_RSP: + if (Ustrncmp(buf, "210", 3) == 0) + break; /* ignore the "210 SCAN DATA" message */ + + if (pcre_exec(ava_re_clean, NULL, CS buf, slen, + 0, 0, ovector, nelements(ovector)) > 0) + break; + + if ((malware_name = m_pcre_exec(ava_re_virus, buf))) + { /* remove backslash in front of [whitespace|backslash] */ + uschar * p, * p0; + for (p = malware_name; *p; ++p) + if (*p == '\\' && (isspace(p[1]) || p[1] == '\\')) + for (p0 = p; *p0; ++p0) *p0 = p0[1]; + + avast_stage = AVA_DONE; + goto endloop; + } -/* simple wrapper for reading lines from sockets */ -int -recv_line(int sock, uschar *buffer, int size) -{ - uschar *p = buffer; + if (Ustrncmp(buf, "200 SCAN OK", 11) == 0) + { /* we're done finally */ + if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */ + return m_errlog_defer_3(scanent, string_sprintf( + "unable to send quit request to socket (%s): %s", + scanner_options, strerror(errno)), + sock); + malware_name = NULL; + avast_stage = AVA_DONE; + goto endloop; + } + + /* here for any unexpected response from the scanner */ + goto endloop; + } + } + } + endloop: + + switch(avast_stage) + { + case AVA_HELO: + case AVA_OPT: + case AVA_RSP: return m_errlog_defer_3(scanent, + nread >= 0 + ? string_sprintf( + "invalid response from scanner: '%s'", buf) + : nread == -1 + ? US"EOF from scanner" + : US"timeout from scanner", + sock); + default: break; + } + } + break; + } /* scanner type switch */ - memset(buffer,0,size); - /* read until \n */ - while(recv(sock,p,1,0) > -1) { - if ((p-buffer) > (size-2)) break; - if (*p == '\n') break; - if (*p != '\r') p++; + if (sock >= 0) + (void) close (sock); + malware_ok = TRUE; /* set "been here, done that" marker */ } - *p = '\0'; - return (p-buffer); +/* match virus name against pattern (caseless ------->----------v) */ +if (malware_name && regex_match_and_setup(re, malware_name, 0, -1)) + { + DEBUG(D_acl) debug_printf( + "Matched regex to malware [%s] [%s]\n", malware_re, malware_name); + return OK; + } +else + return FAIL; } -/* ============= private routines for the "mksd" scanner type ============== */ +/************************************************* +* Scan an email for malware * +*************************************************/ -#include +/* This is the normal interface for scanning an email, which doesn't need a +filename; it's a wrapper around the malware_file function. -static inline int -mksd_writev (int sock, struct iovec *iov, int iovcnt) +Arguments: + malware_re match condition for "malware=" + timeout if nonzero, timeout in seconds + +Returns: Exim message processing code (OK, FAIL, DEFER, ...) + where true means malware was found (condition applies) +*/ +int +malware(const uschar * malware_re, int timeout) { - int i; + uschar * scan_filename; + int ret; - for (;;) { - do - i = writev (sock, iov, iovcnt); - while ((i < 0) && (errno == EINTR)); - if (i <= 0) { - (void) malware_errlog_defer(US"unable to write to mksd UNIX socket (/var/run/mksd/socket)"); - return -1; - } + scan_filename = string_sprintf("%s/scan/%s/%s.eml", + spool_directory, message_id, message_id); + ret = malware_internal(malware_re, scan_filename, timeout, FALSE); + if (ret == DEFER) av_failed = TRUE; - for (;;) - if (i >= iov->iov_len) { - if (--iovcnt == 0) - return 0; - i -= iov->iov_len; - iov++; - } else { - iov->iov_len -= i; - iov->iov_base = CS iov->iov_base + i; - break; - } - } + return ret; } -static inline int -mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size) -{ - int offset = 0; - int i; - do { - if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) { - (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); - return -1; - } +/************************************************* +* Scan a file for malware * +*************************************************/ - offset += i; - /* offset == av_buffer_size -> buffer full */ - if (offset == av_buffer_size) { - (void) malware_errlog_defer(US"malformed reply received from mksd"); - return -1; - } - } while (av_buffer[offset-1] != '\n'); +/* This is a test wrapper for scanning an email, which is not used in +normal processing. Scan any file, using the Exim scanning interface. +This function tampers with various global variables so is unsafe to use +in any other context. - av_buffer[offset] = '\0'; - return offset; -} +Arguments: + eml_filename a file holding the message to be scanned -static inline int -mksd_parse_line(struct scan * scanent, char *line) +Returns: Exim message processing code (OK, FAIL, DEFER, ...) + where true means malware was found (condition applies) +*/ +int +malware_in_file(uschar *eml_filename) { - char *p; + uschar message_id_buf[64]; + int ret; - switch (*line) { - case 'O': /* OK */ - return OK; - - case 'E': - case 'A': /* ERR */ - if ((p = strchr (line, '\n')) != NULL) - *p = '\0'; - return m_errlog_defer(scanent, - string_sprintf("scanner failed: %s", line)); - - default: /* VIR */ - if ((p = strchr (line, '\n')) != NULL) { - *p = '\0'; - if (((p-line) > 5) && (line[3] == ' ')) - if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) { - *p = '\0'; - malware_name = string_copy(US line+4); - return OK; - } - } - return m_errlog_defer(scanent, - string_sprintf("malformed reply received: %s", line)); - } -} + /* spool_mbox() assumes various parameters exist, when creating + the relevant directory and the email within */ + (void) string_format(message_id_buf, sizeof(message_id_buf), + "dummy-%d", vaguely_random_number(INT_MAX)); + message_id = message_id_buf; + sender_address = US"malware-sender@example.net"; + return_path = US""; + recipients_list = NULL; + receive_add_recipient(US"malware-victim@example.net", -1); + enable_dollar_recipients = TRUE; -static int -mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename) -{ - struct iovec iov[3]; - const char *cmd = "MSQ\n"; - uschar av_buffer[1024]; - - iov[0].iov_base = (void *) cmd; - iov[0].iov_len = 3; - iov[1].iov_base = CS scan_filename; - iov[1].iov_len = Ustrlen(scan_filename); - iov[2].iov_base = (void *) (cmd + 3); - iov[2].iov_len = 1; + ret = malware_internal(US"*", eml_filename, 0, TRUE); - if (mksd_writev (sock, iov, 3) < 0) - return DEFER; + Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id)); + spool_mbox_ok = 1; + /* don't set no_mbox_unspool; at present, there's no way for it to become + set, but if that changes, then it should apply to these tests too */ + unspool_mbox(); - if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0) - return DEFER; + /* silence static analysis tools */ + message_id = NULL; - return mksd_parse_line (scanent, CS av_buffer); + return ret; +} + + +void +malware_init(void) +{ +if (!malware_default_re) + malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE); +if (!drweb_re) + drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE); +if (!fsec_re) + fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE); +if (!kav_re_sus) + kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE); +if (!kav_re_inf) + kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE); +if (!ava_re_clean) + ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE); +if (!ava_re_virus) + ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE); } #endif /*WITH_CONTENT_SCAN*/ diff -Nru exim4-4.84/src/match.c exim4-4.86~RC4/src/match.c --- exim4-4.84/src/match.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/match.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for matching strings */ @@ -15,8 +15,8 @@ strings, domains, and local parts. */ typedef struct check_string_block { - uschar *origsubject; /* caseful; keep these two first, in */ - uschar *subject; /* step with the block below */ + const uschar *origsubject; /* caseful; keep these two first, in */ + const uschar *subject; /* step with the block below */ int expand_setup; BOOL use_partial; BOOL caseless; @@ -28,7 +28,7 @@ addresses. */ typedef struct check_address_block { - uschar *origaddress; /* caseful; keep these two first, in */ + const uschar *origaddress; /* caseful; keep these two first, in */ uschar *address; /* step with the block above */ int expand_setup; BOOL caseless; @@ -92,12 +92,12 @@ */ static int -check_string(void *arg, uschar *pattern, uschar **valueptr, uschar **error) +check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) { -check_string_block *cb = (check_string_block *)arg; +const check_string_block *cb = arg; int search_type, partial, affixlen, starflags; int expand_setup = cb->expand_setup; -uschar *affix; +const uschar *affix; uschar *s; uschar *filename = NULL; uschar *keyquery, *result, *semicolon; @@ -111,7 +111,7 @@ it works if the pattern uses (?-i) to turn off case-independence, overriding "caseless". */ -s = (pattern[0] == '^')? cb->origsubject : cb->subject; +s = string_copy(pattern[0] == '^' ? cb->origsubject : cb->subject); /* If required to set up $0, initialize the data but don't turn on by setting expand_nmax until the match is assured. */ @@ -131,7 +131,7 @@ { const pcre *re = regex_must_compile(pattern, cb->caseless, FALSE); return ((expand_setup < 0)? - pcre_exec(re, NULL, CS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) >= 0 + pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) >= 0 : regex_match_and_setup(re, s, 0, expand_setup) )? @@ -192,8 +192,8 @@ BOOL prim = FALSE; BOOL secy = FALSE; BOOL removed = FALSE; - uschar *ss = pattern + 4; - uschar *ignore_target_hosts = NULL; + const uschar *ss = pattern + 4; + const uschar *ignore_target_hosts = NULL; if (strncmpic(ss, US"any", 3) == 0) ss += 3; else if (strncmpic(ss, US"primary", 7) == 0) @@ -221,8 +221,7 @@ NULL, /* service name not relevant */ NULL, /* srv_fail_domains not relevant */ NULL, /* mx_fail_domains not relevant */ - NULL, /* no dnssec request XXX ? */ - NULL, /* no dnssec require XXX ? */ + NULL, /* no dnssec request/require XXX ? */ NULL, /* no feedback FQDN */ &removed); /* feedback if local removed */ @@ -337,8 +336,8 @@ */ int -match_check_string(uschar *s, uschar *pattern, int expand_setup, - BOOL use_partial, BOOL caseless, BOOL at_is_special, uschar **valueptr) +match_check_string(const uschar *s, const uschar *pattern, int expand_setup, + BOOL use_partial, BOOL caseless, BOOL at_is_special, const uschar **valueptr) { check_string_block cb; cb.origsubject = s; @@ -366,7 +365,7 @@ type MCL_STRING, MCL_DOMAIN, MCL_HOST, MCL_ADDRESS, or MCL_LOCALPART */ -static uschar * +static const uschar * get_check_key(void *arg, int type) { switch(type) @@ -436,9 +435,9 @@ */ int -match_check_list(uschar **listptr, int sep, tree_node **anchorptr, - unsigned int **cache_ptr, int (*func)(void *,uschar *,uschar **,uschar **), - void *arg, int type, uschar *name, uschar **valueptr) +match_check_list(const uschar **listptr, int sep, tree_node **anchorptr, + unsigned int **cache_ptr, int (*func)(void *,const uschar *,const uschar **,uschar **), + void *arg, int type, const uschar *name, const uschar **valueptr) { int yield = OK; unsigned int *original_cache_bits = *cache_ptr; @@ -446,7 +445,7 @@ BOOL ignore_unknown = FALSE; BOOL include_defer = FALSE; BOOL ignore_defer = FALSE; -uschar *list; +const uschar *list; uschar *sss; uschar *ot = NULL; uschar buffer[1024]; @@ -489,12 +488,12 @@ if (type == MCL_DOMAIN && deliver_domain == NULL) { check_string_block *cb = (check_string_block *)arg; - deliver_domain = cb->subject; - list = expand_string(*listptr); + deliver_domain = string_copy(cb->subject); + list = expand_cstring(*listptr); deliver_domain = NULL; } - else list = expand_string(*listptr); + else list = expand_cstring(*listptr); if (list == NULL) { @@ -701,7 +700,7 @@ cached = US" - cached"; if (valueptr != NULL) { - uschar *key = get_check_key(arg, type); + const uschar *key = get_check_key(arg, type); namedlist_cacheblock *p; for (p = nb->cache_data; p != NULL; p = p->next) { @@ -740,7 +739,7 @@ case DEFER: if (error == NULL) - error = string_sprintf("DNS lookup of %s deferred", ss); + error = string_sprintf("DNS lookup of \"%s\" deferred", ss); if (ignore_defer) { HDEBUG(D_lists) debug_printf("%s: item ignored by +ignore_defer\n", @@ -752,6 +751,7 @@ log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); return OK; } + if (!search_error_message) search_error_message = error; goto DEFER_RETURN; /* The ERROR return occurs when checking hosts, when either a forward @@ -952,8 +952,9 @@ */ int -match_isinlist(uschar *s, uschar **listptr, int sep, tree_node **anchorptr, - unsigned int *cache_bits, int type, BOOL caseless, uschar **valueptr) +match_isinlist(const uschar *s, const uschar **listptr, int sep, + tree_node **anchorptr, + unsigned int *cache_bits, int type, BOOL caseless, const uschar **valueptr) { unsigned int *local_cache_bits = cache_bits; check_string_block cb; @@ -999,16 +1000,17 @@ */ static int -check_address(void *arg, uschar *pattern, uschar **valueptr, uschar **error) +check_address(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) { check_address_block *cb = (check_address_block *)arg; check_string_block csb; int rc; int expand_inc = 0; unsigned int *null = NULL; -uschar *listptr; +const uschar *listptr; uschar *subject = cb->address; -uschar *s, *pdomain, *sdomain; +const uschar *s; +uschar *pdomain, *sdomain; error = error; /* Keep clever compilers from complaining */ @@ -1070,7 +1072,8 @@ if (pattern[0] == '@' && pattern[1] == '@') { int watchdog = 50; - uschar *list, *key, *ss; + const uschar *key; + uschar *list, *ss; uschar buffer[1024]; if (sdomain == subject + 1 && *subject == '*') return FAIL; @@ -1083,7 +1086,7 @@ int sep = 0; if ((rc = match_check_string(key, pattern + 2, -1, TRUE, FALSE, FALSE, - &list)) != OK) return rc; + CUSS &list)) != OK) return rc; /* Check for chaining from the last item; set up the next key if one is found. */ @@ -1102,8 +1105,7 @@ /* Look up the local parts provided by the list; negation is permitted. If a local part has to begin with !, a regex can be used. */ - while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) - != NULL) + while ((ss = string_nextinlist(CUSS &list, &sep, buffer, sizeof(buffer)))) { int local_yield; @@ -1278,9 +1280,9 @@ */ int -match_address_list(uschar *address, BOOL caseless, BOOL expand, - uschar **listptr, unsigned int *cache_bits, int expand_setup, int sep, - uschar **valueptr) +match_address_list(const uschar *address, BOOL caseless, BOOL expand, + const uschar **listptr, unsigned int *cache_bits, int expand_setup, int sep, + const uschar **valueptr) { uschar *p; check_address_block ab; diff -Nru exim4-4.84/src/mime.c exim4-4.86~RC4/src/mime.c --- exim4-4.84/src/mime.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/mime.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner 2004 */ +/* Copyright (c) Tom Kistner 2004, 2015 */ /* License: GPL */ #include "exim.h" @@ -21,7 +21,7 @@ give info on detected "problems" in MIME encodings. Those are defined in mime.h. */ -void +static void mime_set_anomaly(int level, const char *text) { mime_anomaly_level = level; @@ -41,7 +41,7 @@ 0-255 - char to write */ -uschar * +static uschar * mime_decode_qp_char(uschar *qp_p, int *c) { uschar *initial_pos = qp_p; @@ -240,7 +240,7 @@ } -FILE * +static FILE * mime_get_decode_file(uschar *pname, uschar *fname) { FILE *f = NULL; @@ -283,10 +283,10 @@ int -mime_decode(uschar **listptr) +mime_decode(const uschar **listptr) { int sep = 0; -uschar *list = *listptr; +const uschar *list = *listptr; uschar *option; uschar option_buffer[1024]; uschar decode_path[1024]; @@ -369,7 +369,8 @@ return OK; } -int + +static int mime_get_header(FILE *f, uschar *header) { int c = EOF; @@ -474,21 +475,110 @@ } +static void +mime_vars_reset(void) +{ +mime_anomaly_level = 0; +mime_anomaly_text = NULL; +mime_boundary = NULL; +mime_charset = NULL; +mime_decoded_filename = NULL; +mime_filename = NULL; +mime_content_description = NULL; +mime_content_disposition = NULL; +mime_content_id = NULL; +mime_content_transfer_encoding = NULL; +mime_content_type = NULL; +mime_is_multipart = 0; +mime_content_size = 0; +} + + +/* Grab a parameter value, dealing with quoting. + +Arguments: + str Input string. Updated on return to point to terminating ; or NUL + +Return: + Allocated string with parameter value +*/ +static uschar * +mime_param_val(uschar ** sp) +{ +uschar * s = *sp; +uschar * val = NULL; +int size = 0, ptr = 0; + +/* debug_printf(" considering paramval '%s'\n", s); */ + +while (*s && *s != ';') /* ; terminates */ + if (*s == '"') + { + s++; /* skip opening " */ + while (*s && *s != '"') /* " protects ; */ + val = string_cat(val, &size, &ptr, s++, 1); + if (*s) s++; /* skip closing " */ + } + else + val = string_cat(val, &size, &ptr, s++, 1); +if (val) val[ptr] = '\0'; +*sp = s; +return val; +} + +static uschar * +mime_next_semicolon(uschar * s) +{ +while (*s && *s != ';') /* ; terminates */ + if (*s == '"') + { + s++; /* skip opening " */ + while (*s && *s != '"') /* " protects ; */ + s++; + if (*s) s++; /* skip closing " */ + } + else + s++; +return s; +} + + +static uschar * +rfc2231_to_2047(const uschar * fname, const uschar * charset, int * len) +{ +int size = 0, ptr = 0; +uschar * val = string_cat(NULL, &size, &ptr, US"=?", 2); +uschar c; + +val = string_cat(val, &size, &ptr, charset, Ustrlen(charset)); +val = string_cat(val, &size, &ptr, US"?Q?", 3); + +while ((c = *fname)) + if (c == '%' && isxdigit(fname[1]) && isxdigit(fname[2])) + { + val = string_cat(val, &size, &ptr, US"=", 1); + val = string_cat(val, &size, &ptr, ++fname, 2); + fname += 2; + } + else + val = string_cat(val, &size, &ptr, fname++, 1); + +val = string_cat(val, &size, &ptr, US"?=", 2); +val[*len = ptr] = '\0'; +return val; +} + + int mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *context, - uschar **user_msgptr, uschar **log_msgptr) + uschar **user_msgptr, uschar **log_msgptr) { int rc = OK; -uschar *header = NULL; +uschar * header = NULL; struct mime_boundary_context nested_context; /* reserve a line buffer to work in */ -if (!(header = (uschar *)malloc(MIME_MAX_HEADER_SIZE+1))) - { - log_write(0, LOG_PANIC, - "MIME ACL: can't allocate %d bytes of memory.", MIME_MAX_HEADER_SIZE+1); - return DEFER; - } +header = store_get(MIME_MAX_HEADER_SIZE+1); /* Not actually used at the moment, but will be vital to fixing * some RFC 2046 nonconformance later... */ @@ -498,26 +588,12 @@ while(1) { /* reset all per-part mime variables */ - mime_anomaly_level = 0; - mime_anomaly_text = NULL; - mime_boundary = NULL; - mime_charset = NULL; - mime_decoded_filename = NULL; - mime_filename = NULL; - mime_content_description = NULL; - mime_content_disposition = NULL; - mime_content_id = NULL; - mime_content_transfer_encoding = NULL; - mime_content_type = NULL; - mime_is_multipart = 0; - mime_content_size = 0; - - /* - If boundary is null, we assume that *f is positioned on the start of headers (for example, - at the very beginning of a message. - If a boundary is given, we must first advance to it to reach the start of the next header - block. - */ + mime_vars_reset(); + + /* If boundary is null, we assume that *f is positioned on the start of + headers (for example, at the very beginning of a message. If a boundary is + given, we must first advance to it to reach the start of the next header + block. */ /* NOTE -- there's an error here -- RFC2046 specifically says to * check for outer boundaries. This code doesn't do that, and @@ -526,122 +602,183 @@ * (I have moved partway towards adding support, however, by adding * a "parent" field to my new boundary-context structure.) */ - if (context != NULL) + if (context) for (;;) { - while(fgets(CS header, MIME_MAX_HEADER_SIZE, f) != NULL) + if (!fgets(CS header, MIME_MAX_HEADER_SIZE, f)) { - /* boundary line must start with 2 dashes */ - if (Ustrncmp(header,"--",2) == 0) - { - if (Ustrncmp((header+2),context->boundary,Ustrlen(context->boundary)) == 0) - { - /* found boundary */ - if (Ustrncmp((header+2+Ustrlen(context->boundary)),"--",2) == 0) - { - /* END boundary found */ - debug_printf("End boundary found %s\n", context->boundary); - return rc; - } - else - debug_printf("Next part with boundary %s\n", context->boundary); + /* Hit EOF or read error. Ugh. */ + DEBUG(D_acl) debug_printf("Hit EOF ...\n"); + return rc; + } - /* can't use break here */ - goto DECODE_HEADERS; - } + /* boundary line must start with 2 dashes */ + if ( Ustrncmp(header, "--", 2) == 0 + && Ustrncmp(header+2, context->boundary, Ustrlen(context->boundary)) == 0 + ) + { /* found boundary */ + if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0) + { + /* END boundary found */ + DEBUG(D_acl) debug_printf("End boundary found %s\n", + context->boundary); + return rc; } + + DEBUG(D_acl) debug_printf("Next part with boundary %s\n", + context->boundary); + break; } - /* Hit EOF or read error. Ugh. */ - debug_printf("Hit EOF ...\n"); - return rc; } -DECODE_HEADERS: /* parse headers, set up expansion variables */ - while (mime_get_header(f,header)) + while (mime_get_header(f, header)) { - int i; - /* loop through header list */ - for (i = 0; i < mime_header_list_size; i++) + struct mime_header * mh; + + /* look for interesting headers */ + for (mh = mime_header_list; + mh < mime_header_list + mime_header_list_size; + mh++) if (strncmpic(mh->name, header, mh->namelen) == 0) { - uschar *header_value = NULL; - int header_value_len = 0; + uschar * p = header + mh->namelen; + uschar * q; + + /* grab the value (normalize to lower case) + and copy to its corresponding expansion variable */ + + for (q = p; *q != ';' && *q; q++) ; + *mh->value = string_copynlc(p, q-p); + DEBUG(D_acl) debug_printf("found %s MIME header, value is '%s'\n", + mh->name, *mh->value); + + if (*(p = q)) p++; /* jump past the ; */ - /* found an interesting header? */ - if (strncmpic(mime_header_list[i].name,header,mime_header_list[i].namelen) == 0) { - uschar *p = header + mime_header_list[i].namelen; - /* yes, grab the value (normalize to lower case) - and copy to its corresponding expansion variable */ - while(*p != ';') - { - *p = tolower(*p); - p++; - } - header_value_len = (p - (header + mime_header_list[i].namelen)); - header_value = (uschar *)malloc(header_value_len+1); - memset(header_value,0,header_value_len+1); - p = header + mime_header_list[i].namelen; - Ustrncpy(header_value, p, header_value_len); - debug_printf("Found %s MIME header, value is '%s'\n", mime_header_list[i].name, header_value); - *((uschar **)(mime_header_list[i].value)) = header_value; - - /* make p point to the next character after the closing ';' */ - p += (header_value_len+1); - - /* grab all param=value tags on the remaining line, check if they are interesting */ -NEXT_PARAM_SEARCH: - while (*p != 0) + uschar * mime_fname = NULL; + uschar * mime_fname_rfc2231 = NULL; + uschar * mime_filename_charset = NULL; + BOOL decoding_failed = FALSE; + + /* grab all param=value tags on the remaining line, + check if they are interesting */ + + while (*p) { mime_parameter * mp; - for (mp = mime_parameter_list; - mp < &mime_parameter_list[mime_parameter_list_size]; - mp++) - { - uschar *param_value = NULL; - int param_value_len = 0; - /* found an interesting parameter? */ - if (strncmpic(mp->name, p, mp->namelen) == 0) + DEBUG(D_acl) debug_printf(" considering paramlist '%s'\n", p); + + if ( !mime_filename + && strncmpic(CUS"content-disposition:", header, 20) == 0 + && strncmpic(CUS"filename*", p, 9) == 0 + ) + { /* RFC 2231 filename */ + uschar * q; + + /* find value of the filename */ + p += 9; + while(*p != '=' && *p) p++; + if (*p) p++; /* p is filename or NUL */ + q = mime_param_val(&p); /* p now trailing ; or NUL */ + + if (q && *q) { - uschar *q = p + mp->namelen; - int size = 0; - int ptr = 0; + uschar * temp_string, * err_msg; + int slen; + + /* build up an un-decoded filename over successive + filename*= parameters (for use when 2047 decode fails) */ + + mime_fname_rfc2231 = string_sprintf("%#s%s", + mime_fname_rfc2231, q); - /* yes, grab the value and copy to its corresponding expansion variable */ - while(*q && *q != ';') /* ; terminates */ + if (!decoding_failed) { - if (*q == '"') + int size; + if (!mime_filename_charset) { - q++; /* skip leading " */ - while(*q && *q != '"') /* which protects ; */ - param_value = string_cat(param_value, &size, &ptr, q++, 1); - if (*q) q++; /* skip trailing " */ + uschar * s = q; + + /* look for a ' in the "filename" */ + while(*s != '\'' && *s) s++; /* s is ' or NUL */ + + if ((size = s-q) > 0) + { + mime_filename_charset = string_copyn(q, size); + p = s; + + while(*p == '\'' && *p) p++; /* p is after ' */ + } } else - param_value = string_cat(param_value, &size, &ptr, q++, 1); - } - param_value[ptr++] = '\0'; - param_value_len = ptr; + p = q; + + temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen); + temp_string = rfc2047_decode(temp_string, FALSE, NULL, 32, + NULL, &err_msg); + size = Ustrlen(temp_string); - param_value = rfc2047_decode(param_value, check_rfc2047_length, NULL, 32, ¶m_value_len, &q); - debug_printf("Found %s MIME parameter in %s header, value is '%s'\n", mp->name, mime_header_list[i].name, param_value); - *((uschar **)(mp->value)) = param_value; - p += (mp->namelen + param_value_len + 1); - goto NEXT_PARAM_SEARCH; + if (size == slen) + decoding_failed = TRUE; + else + /* build up a decoded filename over successive + filename*= parameters */ + + mime_filename = mime_fname = mime_fname + ? string_sprintf("%s%s", mime_fname, temp_string) + : temp_string; + } + } } - } + + else + /* look for interesting parameters */ + for (mp = mime_parameter_list; + mp < mime_parameter_list + nelem(mime_parameter_list); + mp++ + ) if (strncmpic(mp->name, p, mp->namelen) == 0) + { + uschar * q; + uschar * dummy_errstr; + + /* grab the value and copy to its expansion variable */ + p += mp->namelen; + q = mime_param_val(&p); /* p now trailing ; or NUL */ + + *mp->value = q && *q + ? rfc2047_decode(q, check_rfc2047_length, NULL, 32, NULL, + &dummy_errstr) + : NULL; + DEBUG(D_acl) debug_printf( + " found %s MIME parameter in %s header, value '%s'\n", + mp->name, mh->name, *mp->value); + + break; /* done matching param names */ + } + + /* There is something, but not one of our interesting parameters. - Advance to the next semicolon */ - while(*p != ';') p++; - p++; + Advance past the next semicolon */ + p = mime_next_semicolon(p); + if (*p) p++; + } /* param scan on line */ + + if (strncmpic(CUS"content-disposition:", header, 20) == 0) + { + if (decoding_failed) mime_filename = mime_fname_rfc2231; + + DEBUG(D_acl) debug_printf( + " found %s MIME parameter in %s header, value is '%s'\n", + "filename", mh->name, mime_filename); + } } } } - } /* set additional flag variables (easier access) */ - if ( (mime_content_type != NULL) && - (Ustrncmp(mime_content_type,"multipart",9) == 0) ) + if ( mime_content_type + && Ustrncmp(mime_content_type,"multipart",9) == 0 + ) mime_is_multipart = 1; /* Make a copy of the boundary pointer. @@ -672,7 +809,8 @@ (nested_context.boundary != NULL) && (Ustrncmp(mime_content_type,"multipart",9) == 0) ) { - debug_printf("Entering multipart recursion, boundary '%s'\n", nested_context.boundary); + DEBUG(D_acl) debug_printf("Entering multipart recursion, boundary '%s'\n", + nested_context.boundary); nested_context.context = context && context->context == MBC_ATTACHMENT @@ -688,7 +826,7 @@ else if ( (mime_content_type != NULL) && (Ustrncmp(mime_content_type,"message/rfc822",14) == 0) ) { - uschar *rfc822name = NULL; + const uschar *rfc822name = NULL; uschar filename[2048]; int file_nr = 0; int result = 0; @@ -718,19 +856,22 @@ { log_write(0, LOG_MAIN, "mime_regex acl condition warning - could not decode RFC822 MIME part to file."); - return DEFER; + rc = DEFER; + goto out; } mime_decoded_filename = NULL; } NO_RFC822: /* If the boundary of this instance is NULL, we are finished here */ - if (context == NULL) break; + if (!context) break; if (context->context == MBC_COVERLETTER_ONESHOT) context->context = MBC_ATTACHMENT; } +out: +mime_vars_reset(); return rc; } diff -Nru exim4-4.84/src/mime.h exim4-4.86~RC4/src/mime.h --- exim4-4.84/src/mime.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/mime.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner 2004 */ +/* Copyright (c) Tom Kistner 2004, 2015 */ /* License: GPL */ #ifdef WITH_CONTENT_SCAN @@ -22,17 +22,17 @@ }; typedef struct mime_header { - uschar *name; - int namelen; - void *value; + uschar * name; + int namelen; + uschar ** value; } mime_header; static mime_header mime_header_list[] = { - { US"content-type:", 13, &mime_content_type }, - { US"content-disposition:", 20, &mime_content_disposition }, + { US"content-type:", 13, &mime_content_type }, + { US"content-disposition:", 20, &mime_content_disposition }, { US"content-transfer-encoding:", 26, &mime_content_transfer_encoding }, - { US"content-id:", 11, &mime_content_id }, - { US"content-description:", 20 , &mime_content_description } + { US"content-id:", 11, &mime_content_id }, + { US"content-description:", 20, &mime_content_description } }; static int mime_header_list_size = sizeof(mime_header_list)/sizeof(mime_header); @@ -40,20 +40,18 @@ typedef struct mime_parameter { - uschar *name; - int namelen; - void *value; + uschar * name; + int namelen; + uschar ** value; } mime_parameter; static mime_parameter mime_parameter_list[] = { - { US"name=", 5, &mime_filename }, + { US"name=", 5, &mime_filename }, { US"filename=", 9, &mime_filename }, - { US"charset=", 8, &mime_charset }, + { US"charset=", 8, &mime_charset }, { US"boundary=", 9, &mime_boundary } }; -static int mime_parameter_list_size = sizeof(mime_parameter_list)/sizeof(mime_parameter); - /* MIME Anomaly list */ #define MIME_ANOMALY_BROKEN_BASE64 2, "Broken BASE64 encoding detected" diff -Nru exim4-4.84/src/moan.c exim4-4.86~RC4/src/moan.c --- exim4-4.84/src/moan.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/moan.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for sending messages to sender or to mailmaster. */ @@ -598,7 +598,7 @@ moan_check_errorcopy(uschar *recipient) { uschar *item, *localpart, *domain; -uschar *listptr = errors_copy; +const uschar *listptr = errors_copy; uschar *yield = NULL; uschar buffer[256]; int sep = 0; @@ -619,8 +619,8 @@ while ((item = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) != NULL) { - uschar *newaddress = item; - uschar *pattern = string_dequote(&newaddress); + const uschar *newaddress = item; + const uschar *pattern = string_dequote(&newaddress); /* If no new address found, just skip this item. */ diff -Nru exim4-4.84/src/mytypes.h exim4-4.86~RC4/src/mytypes.h --- exim4-4.84/src/mytypes.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/mytypes.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -66,6 +66,7 @@ #define US (unsigned char *) #define CUS (const unsigned char *) #define USS (unsigned char **) +#define CUSS (const unsigned char **) /* The C library string functions expect "char *" arguments. Use macros to avoid having to write a cast each time. We do this for string and file diff -Nru exim4-4.84/src/parse.c exim4-4.86~RC4/src/parse.c --- exim4-4.84/src/parse.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/parse.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for parsing addresses */ @@ -550,9 +550,7 @@ { s = read_local_part(s, t, errorptr, FALSE); if (*errorptr == NULL) - { if (*s != term) - { if (*s != '@') *errorptr = string_sprintf("\"@\" or \".\" expected after \"%s\"", t); else @@ -562,8 +560,6 @@ *domainptr = t; s = read_domain(s, t, errorptr); } - } - } return s; } @@ -817,7 +813,7 @@ return NULL; } -return (uschar *)yield; +return yield; /* Use goto (via the macro FAILED) to get to here from a variety of places. We might have an empty address in a group - the caller can choose to ignore @@ -866,11 +862,11 @@ the introduction */ -uschar * -parse_quote_2047(uschar *string, int len, uschar *charset, uschar *buffer, +const uschar * +parse_quote_2047(const uschar *string, int len, uschar *charset, uschar *buffer, int buffer_size, BOOL fold) { -uschar *s = string; +const uschar *s = string; uschar *p, *t; int hlen; BOOL coded = FALSE; @@ -985,12 +981,13 @@ Returns: the fixed RFC822 phrase */ -uschar * -parse_fix_phrase(uschar *phrase, int len, uschar *buffer, int buffer_size) +const uschar * +parse_fix_phrase(const uschar *phrase, int len, uschar *buffer, int buffer_size) { int ch, i; BOOL quoted = FALSE; -uschar *s, *t, *end, *yield; +const uschar *s, *end; +uschar *t, *yield; while (len > 0 && isspace(*phrase)) { phrase++; len--; } if (len > buffer_size/4) return US"Name too long"; @@ -1119,7 +1116,7 @@ else if (ch == '(') { - uschar *ss = s; /* uschar after '(' */ + const uschar *ss = s; /* uschar after '(' */ int level = 1; while(ss < end) { @@ -1245,7 +1242,7 @@ int parse_forward_list(uschar *s, int options, address_item **anchor, - uschar **error, uschar *incoming_domain, uschar *directory, + uschar **error, const uschar *incoming_domain, uschar *directory, error_block **syntax_errors) { int count = 0; diff -Nru exim4-4.84/src/pdkim/pdkim.c exim4-4.86~RC4/src/pdkim/pdkim.c --- exim4-4.84/src/pdkim/pdkim.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/pdkim/pdkim.c 2015-06-27 17:01:28.000000000 +0200 @@ -1,7 +1,7 @@ /* * PDKIM - a RFC4871 (DKIM) implementation * - * Copyright (C) 2009 - 2012 Tom Kistner + * Copyright (C) 2009 - 2015 Tom Kistner * * http://duncanthrax.net/pdkim/ * @@ -231,6 +231,7 @@ char *pdkim_strcat(pdkim_str *str, const char *cstr) { return pdkim_strncat(str, cstr, strlen(cstr)); } + char *pdkim_numcat(pdkim_str *str, unsigned long num) { char minibuf[20]; snprintf(minibuf,20,"%lu",num); @@ -1195,86 +1196,216 @@ return PDKIM_OK; } +/* + * RFC 5322 specifies that header line length SHOULD be no more than 78 + * lets make it so! + * pdkim_headcat + * returns char* + * + * col: this int holds and receives column number (octets since last '\n') + * str: partial string to append to + * pad: padding, split line or space after before or after eg: ";" + * intro: - must join to payload eg "h=", usually the tag name + * payload: eg base64 data - long data can be split arbitrarily. + * + * this code doesn't fold the header in some of the places that RFC4871 + * allows: As per RFC5322(2.2.3) it only folds before or after tag-value + * pairs and inside long values. it also always spaces or breaks after the + * "pad" + * + * no guarantees are made for output given out-of range input. like tag + * names loinger than 78, or bogus col. Input is assumed to be free of line breaks. + */ + +static char *pdkim_headcat(int *col, pdkim_str *str, const char*pad,const char *intro, const char *payload ) { + size_t l; + if( pad) + { + l = strlen(pad); + if( *col + l > 78 ) + { + pdkim_strcat(str, "\r\n\t"); + *col=1; + } + pdkim_strncat(str, pad,l); + *col +=l; + } + + l=(pad?1:0) + (intro?strlen(intro):0 ); + + if( *col + l > 78 ) + { /*can't fit intro - start a new line to make room.*/ + pdkim_strcat(str, "\r\n\t"); + *col=1; + l= intro?strlen(intro):0; + } + + l += payload ? strlen(payload):0 ; + + while(l>77) + { /* this fragment will not fit on a single line */ + if( pad ) + { + pdkim_strcat(str, " "); + *col +=1; + pad=NULL; // only want this once + l--; + } + if( intro ) + { + size_t sl=strlen(intro); + pdkim_strncat(str, intro,sl); + *col +=sl; + l-=sl; + intro=NULL; // only want this once + } + if(payload) + { + size_t sl=strlen(payload); + size_t chomp = *col+sl < 77 ? sl : 78-*col; + pdkim_strncat(str, payload,chomp); + *col +=chomp; + payload+=chomp; + l-=chomp-1; + } + // the while precondition tells us it didn't fit. + pdkim_strcat(str, "\r\n\t"); + *col=1; + } + if( *col + l > 78 ) + { + pdkim_strcat(str, "\r\n\t"); + *col=1; + pad=NULL; + } + + if( pad ) + { + pdkim_strcat(str, " "); + *col +=1; + pad=NULL; + } + + if( intro ) + { + size_t sl=strlen(intro); + pdkim_strncat(str, intro,sl); + *col +=sl; + l-=sl; + intro=NULL; + } + if(payload) + { + size_t sl=strlen(payload); + pdkim_strncat(str, payload,sl); + *col +=sl; + } + + return str->str; +} /* -------------------------------------------------------------------------- */ char *pdkim_create_header(pdkim_signature *sig, int final) { char *rc = NULL; char *base64_bh = NULL; char *base64_b = NULL; + int col=0; pdkim_str *hdr = pdkim_strnew("DKIM-Signature: v="PDKIM_SIGNATURE_VERSION); if (hdr == NULL) return NULL; + pdkim_str *canon_all = pdkim_strnew(pdkim_canons[sig->canon_headers]); + if (canon_all == NULL) goto BAIL; base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len); if (base64_bh == NULL) goto BAIL; + col=strlen(hdr->str); + /* Required and static bits */ if ( - pdkim_strcat(hdr,"; a=") && - pdkim_strcat(hdr,pdkim_algos[sig->algo]) && - pdkim_strcat(hdr,"; q=") && - pdkim_strcat(hdr,pdkim_querymethods[sig->querymethod]) && - pdkim_strcat(hdr,"; c=") && - pdkim_strcat(hdr,pdkim_canons[sig->canon_headers]) && - pdkim_strcat(hdr,"/") && - pdkim_strcat(hdr,pdkim_canons[sig->canon_body]) && - pdkim_strcat(hdr,"; d=") && - pdkim_strcat(hdr,sig->domain) && - pdkim_strcat(hdr,"; s=") && - pdkim_strcat(hdr,sig->selector) && - pdkim_strcat(hdr,";\r\n\th=") && - pdkim_strcat(hdr,sig->headernames) && - pdkim_strcat(hdr,"; bh=") && - pdkim_strcat(hdr,base64_bh) && - pdkim_strcat(hdr,";\r\n\t") + pdkim_headcat(&col,hdr,";","a=",pdkim_algos[sig->algo]) && + pdkim_headcat(&col,hdr,";","q=",pdkim_querymethods[sig->querymethod]) && + pdkim_strcat(canon_all,"/") && + pdkim_strcat(canon_all,pdkim_canons[sig->canon_body]) && + pdkim_headcat(&col,hdr,";","c=",canon_all->str) && + pdkim_headcat(&col,hdr,";","d=",sig->domain) && + pdkim_headcat(&col,hdr,";","s=",sig->selector) ) { + /* list of eader names can be split between items. */ + { + char *n=strdup(sig->headernames); + char *f=n; + char *i="h="; + char *s=";"; + if(!n) goto BAIL; + while (*n) + { + char *c=strchr(n,':'); + if(c) *c='\0'; + if(!i) + { + if (!pdkim_headcat(&col,hdr,NULL,NULL,":")) + { + free(f); + goto BAIL; + } + } + if( !pdkim_headcat(&col,hdr,s,i,n)) + { + free(f); + goto BAIL; + } + if(c) n=c+1 ; else break; + s=NULL; + i=NULL; + } + free(f); + } + if(!pdkim_headcat(&col,hdr,";","bh=",base64_bh)) + goto BAIL; + /* Optional bits */ if (sig->identity != NULL) { - if (!( pdkim_strcat(hdr,"i=") && - pdkim_strcat(hdr,sig->identity) && - pdkim_strcat(hdr,";") ) ) { + if(!pdkim_headcat(&col,hdr,";","i=",sig->identity)){ goto BAIL; } } + if (sig->created > 0) { - if (!( pdkim_strcat(hdr,"t=") && - pdkim_numcat(hdr,sig->created) && - pdkim_strcat(hdr,";") ) ) { + char minibuf[20]; + snprintf(minibuf,20,"%lu",sig->created); + if(!pdkim_headcat(&col,hdr,";","t=",minibuf)) { goto BAIL; } } if (sig->expires > 0) { - if (!( pdkim_strcat(hdr,"x=") && - pdkim_numcat(hdr,sig->expires) && - pdkim_strcat(hdr,";") ) ) { + char minibuf[20]; + snprintf(minibuf,20,"%lu",sig->expires); + if(!pdkim_headcat(&col,hdr,";","x=",minibuf)) { goto BAIL; } } if (sig->bodylength >= 0) { - if (!( pdkim_strcat(hdr,"l=") && - pdkim_numcat(hdr,sig->bodylength) && - pdkim_strcat(hdr,";") ) ) { + char minibuf[20]; + snprintf(minibuf,20,"%lu",sig->bodylength); + if(!pdkim_headcat(&col,hdr,";","l=",minibuf)) { goto BAIL; } } - /* Extra linebreak */ - if (hdr->str[(hdr->len)-1] == ';') { - if (!pdkim_strcat(hdr," \r\n\t")) goto BAIL; - } + /* Preliminary or final version? */ if (final) { base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len); if (base64_b == NULL) goto BAIL; - if ( - pdkim_strcat(hdr,"b=") && - pdkim_strcat(hdr,base64_b) && - pdkim_strcat(hdr,";") - ) goto DONE; + if(!pdkim_headcat(&col,hdr,";","b=",base64_b)) goto BAIL; } else { - if (pdkim_strcat(hdr,"b=;")) goto DONE; + if(!pdkim_headcat(&col,hdr,";","b=","")) goto BAIL; } - goto BAIL; + /* add trailing semicolon: I'm not sure if this is actually needed */ + if(!pdkim_headcat(&col,hdr,NULL,";","")) goto BAIL; + + goto DONE; } DONE: @@ -1282,6 +1413,7 @@ BAIL: pdkim_strfree(hdr); + if (canon_all != NULL) pdkim_strfree(canon_all); if (base64_bh != NULL) free(base64_bh); if (base64_b != NULL) free(base64_b); return rc; diff -Nru exim4-4.84/src/queue.c exim4-4.86~RC4/src/queue.c --- exim4-4.84/src/queue.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/queue.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions that operate on the input queue. */ @@ -1274,6 +1274,9 @@ { if (action == MSG_ADD_RECIPIENT) { +#ifdef EXPERIMENTAL_INTERNATIONAL + if (string_is_utf8(recipient)) allow_utf8_domains = message_smtputf8 = TRUE; +#endif receive_add_recipient(recipient, -1); log_write(0, LOG_MAIN, "recipient <%s> added by %s", recipient, username); @@ -1297,6 +1300,9 @@ } else /* MSG_EDIT_SENDER */ { +#ifdef EXPERIMENTAL_INTERNATIONAL + if (string_is_utf8(recipient)) allow_utf8_domains = message_smtputf8 = TRUE; +#endif sender_address = recipient; log_write(0, LOG_MAIN, "sender address changed to <%s> by %s", recipient, username); @@ -1345,7 +1351,8 @@ BOOL *set; int sep = 0; struct stat statbuf; -uschar *s, *ss, *name; +const uschar *s; +uschar *ss, *name; uschar buffer[1024]; if (queue_only_file == NULL) return; diff -Nru exim4-4.84/src/rda.c exim4-4.86~RC4/src/rda.c --- exim4-4.84/src/rda.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/rda.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module contains code for extracting addresses from a forwarding list @@ -444,7 +444,7 @@ */ static int -rda_write_string(int fd, uschar *s) +rda_write_string(int fd, const uschar *s) { int len = (s == NULL)? 0 : Ustrlen(s) + 1; return ( write(fd, &len, sizeof(int)) != sizeof(int) @@ -723,7 +723,7 @@ != sizeof(addr->mode) || write(fd, &(addr->flags), sizeof(addr->flags)) != sizeof(addr->flags) - || rda_write_string(fd, addr->p.errors_address) != 0 + || rda_write_string(fd, addr->prop.errors_address) != 0 ) goto bad; @@ -892,7 +892,7 @@ if (read(fd, &(addr->mode), sizeof(addr->mode)) != sizeof(addr->mode) || read(fd, &(addr->flags), sizeof(addr->flags)) != sizeof(addr->flags) || - !rda_read_string(fd, &(addr->p.errors_address))) goto DISASTER; + !rda_read_string(fd, &(addr->prop.errors_address))) goto DISASTER; /* Next comes a possible setting for $thisaddress and any numerical variables for pipe expansion, terminated by a NULL string. The maximum diff -Nru exim4-4.84/src/readconf.c exim4-4.86~RC4/src/readconf.c --- exim4-4.84/src/readconf.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/readconf.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading the configuration file, and for displaying @@ -11,6 +11,9 @@ #include "exim.h" +static void fn_smtp_receive_timeout(const uschar * name, const uschar * str); + + #define CSTATE_STACK_SIZE 10 @@ -225,17 +228,19 @@ { "dns_ipv4_lookup", opt_stringptr, &dns_ipv4_lookup }, { "dns_retrans", opt_time, &dns_retrans }, { "dns_retry", opt_int, &dns_retry }, + { "dns_trust_aa", opt_stringptr, &dns_trust_aa }, { "dns_use_edns0", opt_int, &dns_use_edns0 }, /* This option is now a no-op, retained for compability */ { "drop_cr", opt_bool, &drop_cr }, /*********************************************************/ -#ifdef EXPERIMENTAL_DSN { "dsn_advertise_hosts", opt_stringptr, &dsn_advertise_hosts }, -#endif { "dsn_from", opt_stringptr, &dsn_from }, { "envelope_to_remove", opt_bool, &envelope_to_remove }, { "errors_copy", opt_stringptr, &errors_copy }, { "errors_reply_to", opt_stringptr, &errors_reply_to }, +#ifdef EXPERIMENTAL_EVENT + { "event_action", opt_stringptr, &event_action }, +#endif { "exim_group", opt_gid, &exim_gid }, { "exim_path", opt_stringptr, &exim_path }, { "exim_user", opt_uid, &exim_uid }, @@ -369,6 +374,7 @@ { "rfc1413_hosts", opt_stringptr, &rfc1413_hosts }, { "rfc1413_query_timeout", opt_time, &rfc1413_query_timeout }, { "sender_unqualified_hosts", opt_stringptr, &sender_unqualified_hosts }, + { "slow_lookup_log", opt_int, &slow_lookup_log }, { "smtp_accept_keepalive", opt_bool, &smtp_accept_keepalive }, { "smtp_accept_max", opt_int, &smtp_accept_max }, { "smtp_accept_max_nonmail", opt_int, &smtp_accept_max_nonmail }, @@ -391,9 +397,12 @@ { "smtp_ratelimit_hosts", opt_stringptr, &smtp_ratelimit_hosts }, { "smtp_ratelimit_mail", opt_stringptr, &smtp_ratelimit_mail }, { "smtp_ratelimit_rcpt", opt_stringptr, &smtp_ratelimit_rcpt }, - { "smtp_receive_timeout", opt_time, &smtp_receive_timeout }, + { "smtp_receive_timeout", opt_func, &fn_smtp_receive_timeout }, { "smtp_reserve_hosts", opt_stringptr, &smtp_reserve_hosts }, { "smtp_return_error_details",opt_bool, &smtp_return_error_details }, +#ifdef EXPERIMENTAL_INTERNATIONAL + { "smtputf8_advertise_hosts", opt_stringptr, &smtputf8_advertise_hosts }, +#endif #ifdef WITH_CONTENT_SCAN { "spamd_address", opt_stringptr, &spamd_address }, #endif @@ -440,6 +449,7 @@ { "tls_crl", opt_stringptr, &tls_crl }, { "tls_dh_max_bits", opt_int, &tls_dh_max_bits }, { "tls_dhparam", opt_stringptr, &tls_dhparam }, + { "tls_eccurve", opt_stringptr, &tls_eccurve }, # ifndef DISABLE_OCSP { "tls_ocsp_file", opt_stringptr, &tls_ocsp_file }, # endif @@ -494,7 +504,7 @@ for (r = routers; r != NULL; r = r->next) { router_info *ri = r->info; - for (i = 0; i < ri->options_count[0]; i++) + for (i = 0; i < *ri->options_count; i++) { if ((ri->options[i].type & opt_mask) != opt_stringptr) continue; if (p == (char *)(r->options_block) + (long int)(ri->options[i].value)) @@ -505,11 +515,16 @@ for (t = transports; t != NULL; t = t->next) { transport_info *ti = t->info; - for (i = 0; i < ti->options_count[0]; i++) + for (i = 0; i < *ti->options_count; i++) { - if ((ti->options[i].type & opt_mask) != opt_stringptr) continue; - if (p == (char *)(t->options_block) + (long int)(ti->options[i].value)) - return US ti->options[i].name; + optionlist * op = &ti->options[i]; + if ((op->type & opt_mask) != opt_stringptr) continue; + if (p == ( op->type & opt_public + ? (char *)t + : (char *)t->options_block + ) + + (long int)op->value) + return US op->name; } } @@ -1021,7 +1036,7 @@ */ int -readconf_readtime(uschar *s, int terminator, BOOL return_msec) +readconf_readtime(const uschar *s, int terminator, BOOL return_msec) { int yield = 0; for (;;) @@ -1030,7 +1045,7 @@ double fraction; if (!isdigit(*s)) return -1; - (void)sscanf(CS s, "%d%n", &value, &count); + (void)sscanf(CCS s, "%d%n", &value, &count); s += count; switch (*s) @@ -1044,7 +1059,7 @@ case '.': if (!return_msec) return -1; - (void)sscanf(CS s, "%lf%n", &fraction, &count); + (void)sscanf(CCS s, "%lf%n", &fraction, &count); s += count; if (*s++ != 's') return -1; yield += (int)(fraction * 1000.0); @@ -1076,7 +1091,7 @@ */ static int -readconf_readfixed(uschar *s, int terminator) +readconf_readfixed(const uschar *s, int terminator) { int yield = 0; int value, count; @@ -1182,7 +1197,7 @@ */ static void -extra_chars_error(uschar *s, uschar *t1, uschar *t2, uschar *t3) +extra_chars_error(const uschar *s, const uschar *t1, const uschar *t2, const uschar *t3) { uschar *comment = US""; if (*s == '#') comment = US" (# is comment only at line start)"; @@ -1218,7 +1233,7 @@ */ static rewrite_rule * -readconf_one_rewrite(uschar *p, int *existflags, BOOL isglobal) +readconf_one_rewrite(const uschar *p, int *existflags, BOOL isglobal) { rewrite_rule *next = store_get(sizeof(rewrite_rule)); @@ -1324,10 +1339,10 @@ */ static uschar * -read_string(uschar *s, uschar *name) +read_string(const uschar *s, const uschar *name) { uschar *yield; -uschar *ss; +const uschar *ss; if (*s != '\"') return string_copy(s); @@ -1345,6 +1360,24 @@ /************************************************* +* Custom-handler options * +*************************************************/ +static void +fn_smtp_receive_timeout(const uschar * name, const uschar * str) +{ +if (*str == '$') + smtp_receive_timeout_s = string_copy(str); +else + { + /* "smtp_receive_timeout", opt_time, &smtp_receive_timeout */ + smtp_receive_timeout = readconf_readtime(str, 0, FALSE); + if (smtp_receive_timeout < 0) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s", + name); + } +} + +/************************************************* * Handle option line * *************************************************/ @@ -1480,7 +1513,7 @@ } /* If a boolean wasn't preceded by "no[t]_" it can be followed by = and -true/false/yes/no, or, in the case of opt_expanded_bool, a general string that +true/false/yes/no, or, in the case of opt_expand_bool, a general string that ultimately expands to one of those values. */ else if (*s != 0 && (offset != 0 || *s != '=')) @@ -1568,18 +1601,16 @@ } else if (ol->type & opt_rep_str) { - uschar sep = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':'; - uschar * cp; - - /* Strip trailing whitespace and seperators */ - for (cp = sptr + Ustrlen(sptr) - 1; - cp >= sptr && (*cp == '\n' || *cp == '\t' || *cp == ' ' || *cp == sep); - cp--) *cp = '\0'; - - if (cp >= sptr) - *str_target = string_copy_malloc( - *str_target ? string_sprintf("%s%c%s", *str_target, sep, sptr) - : sptr); + uschar sep_o = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':'; + int sep_i = -(int)sep_o; + const uschar * list = sptr; + uschar * s; + uschar * list_o = *str_target; + + while ((s = string_nextinlist(&list, &sep_i, NULL, 0))) + list_o = string_append_listele(list_o, sep_o, s); + if (list_o) + *str_target = string_copy_malloc(list_o); } else { @@ -1622,8 +1653,7 @@ flagptr = (int *)((uschar *)data_block + (long int)(ol3->value)); } - while ((p = string_nextinlist(&sptr, &sep, big_buffer, BIG_BUFFER_SIZE)) - != NULL) + while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE))) { rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE); *chain = next; @@ -1747,8 +1777,8 @@ int count = 1; uid_t *list; int ptr = 0; - uschar *p; - uschar *op = expand_string (sptr); + const uschar *p; + const uschar *op = expand_string (sptr); if (op == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", @@ -1788,8 +1818,8 @@ int count = 1; gid_t *list; int ptr = 0; - uschar *p; - uschar *op = expand_string (sptr); + const uschar *p; + const uschar *op = expand_string (sptr); if (op == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", @@ -2110,9 +2140,15 @@ name); if (count > 0 && list[2] == 0) count = 0; list[1] = count; + break; } - break; + case opt_func: + { + void (*fn)() = ol->value; + fn(name, s); + break; + } } return TRUE; @@ -2933,7 +2969,7 @@ int sep = 0; struct stat statbuf; uschar *s, *filename; -uschar *list = config_main_filelist; +const uschar *list = config_main_filelist; /* Loop through the possible file names */ @@ -3000,7 +3036,12 @@ if (config_file != NULL) { + uschar *p; config_filename = config_main_filename = string_copy(filename); + + p = Ustrrchr(filename, '/'); + config_main_directory = p ? string_copyn(filename, p - filename) + : string_copy(US"."); } else { @@ -3100,7 +3141,7 @@ if (primary_hostname == NULL) { - uschar *hostname; + const uschar *hostname; struct utsname uts; if (uname(&uts) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "uname() failed to yield host name"); @@ -3113,8 +3154,8 @@ #if HAVE_IPV6 if (!disable_ipv6 && (dns_ipv4_lookup == NULL || - match_isinlist(hostname, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK)) + match_isinlist(hostname, CUSS &dns_ipv4_lookup, 0, NULL, NULL, + MCL_DOMAIN, TRUE, NULL) != OK)) af = AF_INET6; #else af = AF_INET; @@ -3174,7 +3215,7 @@ if (*log_file_path != 0) { - uschar *ss, *sss; + const uschar *ss, *sss; int sep = ':'; /* Fixed for log file path */ s = expand_string(log_file_path); if (s == NULL) @@ -3661,10 +3702,11 @@ */ uschar * -readconf_retry_error(uschar *pp, uschar *p, int *basic_errno, int *more_errno) +readconf_retry_error(const uschar *pp, const uschar *p, + int *basic_errno, int *more_errno) { int len; -uschar *q = pp; +const uschar *q = pp; while (q < p && *q != '_') q++; len = q - pp; @@ -3693,7 +3735,7 @@ { int i; int xlen = p - q - 1; - uschar *x = q + 1; + const uschar *x = q + 1; static uschar *extras[] = { US"A", US"MX", US"connect", US"connect_A", US"connect_MX" }; @@ -3701,24 +3743,19 @@ { 'A', 'M', RTEF_CTOUT, RTEF_CTOUT|'A', RTEF_CTOUT|'M' }; for (i = 0; i < sizeof(extras)/sizeof(uschar *); i++) - { if (strncmpic(x, extras[i], xlen) == 0) { *more_errno = values[i]; break; } - } if (i >= sizeof(extras)/sizeof(uschar *)) - { if (strncmpic(x, US"DNS", xlen) == 0) - { log_write(0, LOG_MAIN|LOG_PANIC, "\"timeout_dns\" is no longer " "available in retry rules (it has never worked) - treated as " "\"timeout\""); - } - else return US"\"A\", \"MX\", or \"connect\" expected after \"timeout\""; - } + else + return US"\"A\", \"MX\", or \"connect\" expected after \"timeout\""; } } @@ -3745,8 +3782,8 @@ return string_sprintf("%.4s_4 must be followed by xx, dx, or dd, where " "x is literal and d is any digit", pp); - *basic_errno = (*pp == 'm')? ERRNO_MAIL4XX : - (*pp == 'r')? ERRNO_RCPT4XX : ERRNO_DATA4XX; + *basic_errno = *pp == 'm' ? ERRNO_MAIL4XX : + *pp == 'r' ? ERRNO_RCPT4XX : ERRNO_DATA4XX; *more_errno = x << 8; } @@ -3760,6 +3797,9 @@ else if (strncmpic(pp, US"tls_required", p - pp) == 0) *basic_errno = ERRNO_TLSREQUIRED; +else if (strncmpic(pp, US"lookup", p - pp) == 0) + *basic_errno = ERRNO_UNKNOWNHOST; + else if (len != 1 || Ustrncmp(pp, "*", 1) != 0) return string_sprintf("unknown or malformed retry error \"%.*s\"", (int) (p-pp), pp); @@ -3797,10 +3837,10 @@ */ static int -retry_arg(uschar **paddr, int type) +retry_arg(const uschar **paddr, int type) { -uschar *p = *paddr; -uschar *pp; +const uschar *p = *paddr; +const uschar *pp; if (*p++ != ',') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma expected"); @@ -3814,10 +3854,8 @@ *paddr = p; switch (type) { - case 0: - return readconf_readtime(pp, *p, FALSE); - case 1: - return readconf_readfixed(pp, *p); + case 0: return readconf_readtime(pp, *p, FALSE); + case 1: return readconf_readfixed(pp, *p); } return 0; /* Keep picky compilers happy */ } @@ -3829,12 +3867,13 @@ { retry_config **chain = &retries; retry_config *next; -uschar *p; +const uschar *p; -while ((p = get_config_line()) != NULL) +while ((p = get_config_line())) { retry_rule **rchain; - uschar *pp, *error; + const uschar *pp; + uschar *error; next = store_get(sizeof(retry_config)); next->next = NULL; @@ -3854,8 +3893,8 @@ /* Test error names for things we understand. */ - if ((error = readconf_retry_error(pp, p, &(next->basic_errno), - &(next->more_errno))) != NULL) + if ((error = readconf_retry_error(pp, p, &next->basic_errno, + &next->more_errno))) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%s", error); /* There may be an optional address list of senders to be used as another @@ -3892,18 +3931,18 @@ switch (rule->rule) { case 'F': /* Fixed interval */ - rule->p1 = retry_arg(&p, 0); - break; + rule->p1 = retry_arg(&p, 0); + break; case 'G': /* Geometrically increasing intervals */ case 'H': /* Ditto, but with randomness */ - rule->p1 = retry_arg(&p, 0); - rule->p2 = retry_arg(&p, 1); - break; + rule->p1 = retry_arg(&p, 0); + rule->p2 = retry_arg(&p, 1); + break; default: - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "unknown retry rule letter"); - break; + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "unknown retry rule letter"); + break; } if (rule->timeout <= 0 || rule->p1 <= 0 || diff -Nru exim4-4.84/src/receive.c exim4-4.86~RC4/src/receive.c --- exim4-4.84/src/receive.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/receive.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for receiving a message and setting up spool files. */ @@ -87,7 +87,7 @@ qnewsender = (Ustrchr(newsender, '@') != NULL)? newsender : string_sprintf("%s@%s", newsender, qualify_domain_sender); return - match_address_list(qnewsender, TRUE, TRUE, &untrusted_set_sender, NULL, -1, + match_address_list(qnewsender, TRUE, TRUE, CUSS &untrusted_set_sender, NULL, -1, 0, NULL) == OK; } @@ -142,17 +142,16 @@ else { int sep = ':'; /* Not variable - outside scripts use */ - uschar *p = log_file_path; + const uschar *p = log_file_path; name = US"log"; /* An empty log_file_path means "use the default". This is the same as an empty item in a list. */ if (*p == 0) p = US":"; - while ((path = string_nextinlist(&p, &sep, buffer, sizeof(buffer))) != NULL) - { - if (Ustrcmp(path, "syslog") != 0) break; - } + while ((path = string_nextinlist(&p, &sep, buffer, sizeof(buffer)))) + if (Ustrcmp(path, "syslog") != 0) + break; if (path == NULL) /* No log files */ { @@ -497,10 +496,8 @@ /* reset optin string pointer for next recipient */ bmi_current_optin = NULL; #endif -#ifdef EXPERIMENTAL_DSN recipients_list[recipients_count].orcpt = NULL; recipients_list[recipients_count].dsn_flags = 0; -#endif recipients_list[recipients_count++].errors_to = NULL; } @@ -999,7 +996,7 @@ case ACL_WHERE_DKIM: case ACL_WHERE_MIME: case ACL_WHERE_DATA: - if (cutthrough_fd >= 0 && (acl_removed_headers || acl_added_headers)) + if (cutthrough.fd >= 0 && (acl_removed_headers || acl_added_headers)) { log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs" " will not take effect on cutthrough deliveries"); @@ -1011,29 +1008,19 @@ { DEBUG(D_receive|D_acl) debug_printf(">>Headers removed by %s ACL:\n", acl_name); - for (h = header_list; h != NULL; h = h->next) + for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) { - uschar *list; - BOOL include_header; - - if (h->type == htype_old) continue; - - include_header = TRUE; - list = acl_removed_headers; - + const uschar * list = acl_removed_headers; int sep = ':'; /* This is specified as a colon-separated list */ uschar *s; uschar buffer[128]; - while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) - != NULL) - { - int len = Ustrlen(s); - if (header_testname(h, s, len, FALSE)) + + while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) + if (header_testname(h, s, Ustrlen(s), FALSE)) { h->type = htype_old; DEBUG(D_receive|D_acl) debug_printf(" %s", h->text); } - } } acl_removed_headers = NULL; DEBUG(D_receive|D_acl) debug_printf(">>\n"); @@ -1242,42 +1229,40 @@ if (rc == OK) { uschar temp_path[1024]; - int n; - struct dirent *entry; - DIR *tempdir; + struct dirent * entry; + DIR * tempdir; - (void)string_format(temp_path, 1024, "%s/scan/%s", spool_directory, - message_id); + (void) string_format(temp_path, sizeof(temp_path), "%s/scan/%s", + spool_directory, message_id); tempdir = opendir(CS temp_path); - n = 0; - do + for (;;) { - entry = readdir(tempdir); - if (entry == NULL) break; - if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0) + if (!(entry = readdir(tempdir))) + break; + if (strncmpic(US entry->d_name, US"__rfc822_", 9) == 0) { - (void)string_format(rfc822_file_path, 2048,"%s/scan/%s/%s", spool_directory, message_id, entry->d_name); - debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path); + (void) string_format(rfc822_file_path, sizeof(rfc822_file_path), + "%s/scan/%s/%s", spool_directory, message_id, entry->d_name); + debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", + rfc822_file_path); break; } - } while (1); + } closedir(tempdir); - if (entry != NULL) + if (entry) { - mbox_file = Ufopen(rfc822_file_path,"rb"); - if (mbox_file == NULL) + if ((mbox_file = Ufopen(rfc822_file_path, "rb"))) { - log_write(0, LOG_PANIC, - "acl_smtp_mime: can't open RFC822 spool file, skipping."); - unlink(CS rfc822_file_path); - goto END_MIME_ACL; + /* set RFC822 expansion variable */ + mime_is_rfc822 = 1; + mime_part_count_buffer = mime_part_count; + goto MIME_ACL_CHECK; } - /* set RFC822 expansion variable */ - mime_is_rfc822 = 1; - mime_part_count_buffer = mime_part_count; - goto MIME_ACL_CHECK; + log_write(0, LOG_PANIC, + "acl_smtp_mime: can't open RFC822 spool file, skipping."); + unlink(CS rfc822_file_path); } } @@ -2318,9 +2303,23 @@ pp = recipient = store_get(ss - s + 1); for (p = s; p < ss; p++) if (*p != '\n') *pp++ = *p; *pp = 0; + +#ifdef EXPERIMENTAL_INTERNATIONAL + { + BOOL b = allow_utf8_domains; + allow_utf8_domains = TRUE; +#endif recipient = parse_extract_address(recipient, &errmess, &start, &end, &domain, FALSE); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (string_is_utf8(recipient)) + message_smtputf8 = TRUE; + else + allow_utf8_domains = b; + } +#endif + /* Keep a list of all the bad addresses so we can send a single error message at the end. However, an empty address is not an error; just ignore it. This can come from an empty group list like @@ -2821,12 +2820,11 @@ } /* Cutthrough delivery: - We have to create the Received header now rather than at the end of reception, - so the timestamp behaviour is a change to the normal case. - XXX Ensure this gets documented XXX. - Having created it, send the headers to the destination. -*/ -if (cutthrough_fd >= 0) +We have to create the Received header now rather than at the end of reception, +so the timestamp behaviour is a change to the normal case. +XXX Ensure this gets documented XXX. +Having created it, send the headers to the destination. */ +if (cutthrough.fd >= 0) { if (received_count > received_headers_max) { @@ -3188,7 +3186,7 @@ else { int sep = 0; - uschar *ptr = dkim_verify_signers_expanded; + const uschar *ptr = dkim_verify_signers_expanded; uschar *item = NULL; uschar *seen_items = NULL; int seen_items_size = 0; @@ -3198,56 +3196,61 @@ rc = OK; while ((item = string_nextinlist(&ptr, &sep, itembuf, - sizeof(itembuf))) != NULL) + sizeof(itembuf)))) { /* Prevent running ACL for an empty item */ if (!item || (item[0] == '\0')) continue; - /* Only run ACL once for each domain or identity, no matter how often it - appears in the expanded list. */ - if (seen_items != NULL) + + /* Only run ACL once for each domain or identity, + no matter how often it appears in the expanded list. */ + if (seen_items) { uschar *seen_item = NULL; uschar seen_item_buf[256]; - uschar *seen_items_list = seen_items; - int seen_this_item = 0; + const uschar *seen_items_list = seen_items; + BOOL seen_this_item = FALSE; while ((seen_item = string_nextinlist(&seen_items_list, &sep, seen_item_buf, - sizeof(seen_item_buf))) != NULL) - { - if (Ustrcmp(seen_item,item) == 0) - { - seen_this_item = 1; - break; - } - } + sizeof(seen_item_buf)))) + if (Ustrcmp(seen_item,item) == 0) + { + seen_this_item = TRUE; + break; + } - if (seen_this_item > 0) + if (seen_this_item) { DEBUG(D_receive) - debug_printf("acl_smtp_dkim: skipping signer %s, already seen\n", item); + debug_printf("acl_smtp_dkim: skipping signer %s, " + "already seen\n", item); continue; } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":"); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, ":"); } - seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,item); + seen_items = string_append(seen_items, &seen_items_size, + &seen_items_offset, 1, item); seen_items[seen_items_offset] = '\0'; DEBUG(D_receive) - debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", item); + debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", + item); dkim_exim_acl_setup(item); - rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, &user_msg, &log_msg); + rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, + &user_msg, &log_msg); if (rc != OK) - { - DEBUG(D_receive) - debug_printf("acl_smtp_dkim: acl_check returned %d on %s, skipping remaining items\n", rc, item); - cancel_cutthrough_connection("dkim acl not ok"); - break; - } + { + DEBUG(D_receive) + debug_printf("acl_smtp_dkim: acl_check returned %d on %s, " + "skipping remaining items\n", rc, item); + cancel_cutthrough_connection("dkim acl not ok"); + break; + } } add_acl_headers(ACL_WHERE_DKIM, US"DKIM"); if (rc == DISCARD) @@ -3971,7 +3974,7 @@ XXX We do not handle queue-only, freezing, or blackholes. */ -if(cutthrough_fd >= 0) +if(cutthrough.fd >= 0) { uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */ /* Logging was done in finaldot() */ @@ -4117,7 +4120,7 @@ case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */ default:break; } - cutthrough_delivery = FALSE; + cutthrough.delivery = FALSE; } /* For batched SMTP, generate an error message on failure, and do @@ -4135,9 +4138,9 @@ if (blackholed_by != NULL) { - uschar *detail = (local_scan_data != NULL)? - string_printing(local_scan_data) : - string_sprintf("(%s discarded recipients)", blackholed_by); + const uschar *detail = local_scan_data + ? string_printing(local_scan_data) + : string_sprintf("(%s discarded recipients)", blackholed_by); log_write(0, LOG_MAIN, "=> blackhole %s%s", detail, blackhole_log_msg); log_write(0, LOG_MAIN, "Completed"); message_id[0] = 0; diff -Nru exim4-4.84/src/regex.c exim4-4.86~RC4/src/regex.c --- exim4-4.84/src/regex.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/regex.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner 2003-???? */ +/* Copyright (c) Tom Kistner 2003-2015 */ /* License: GPL */ /* Code for matching regular expressions against headers and body. @@ -25,9 +25,11 @@ extern FILE *mime_stream; extern uschar *mime_current_boundary; -int regex(uschar **listptr) { +int +regex(const uschar **listptr) +{ int sep = 0; - uschar *list = *listptr; + const uschar *list = *listptr; uschar *regex_string; uschar regex_string_buffer[1024]; unsigned long mbox_size; @@ -138,9 +140,11 @@ } -int mime_regex(uschar **listptr) { +int +mime_regex(const uschar **listptr) +{ int sep = 0; - uschar *list = *listptr; + const uschar *list = *listptr; uschar *regex_string; uschar regex_string_buffer[1024]; pcre *re; @@ -195,7 +199,7 @@ /* check if the file is already decoded */ if (mime_decoded_filename == NULL) { - uschar *empty = US""; + const uschar *empty = US""; /* no, decode it first */ mime_decode(&empty); if (mime_decoded_filename == NULL) { @@ -242,4 +246,4 @@ return FAIL; } -#endif +#endif /* WITH_CONTENT_SCAN */ diff -Nru exim4-4.84/src/retry.c exim4-4.86~RC4/src/retry.c --- exim4-4.84/src/retry.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/retry.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with retrying unsuccessful deliveries. */ @@ -29,7 +29,7 @@ */ BOOL -retry_ultimate_address_timeout(uschar *retry_key, uschar *domain, +retry_ultimate_address_timeout(uschar *retry_key, const uschar *domain, dbdata_retry *retry_record, time_t now) { BOOL address_timeout; @@ -122,7 +122,7 @@ */ BOOL -retry_check_address(uschar *domain, host_item *host, uschar *portstring, +retry_check_address(const uschar *domain, host_item *host, uschar *portstring, BOOL include_ip_address, uschar **retry_host_key, uschar **retry_message_key) { BOOL yield = FALSE; @@ -294,12 +294,15 @@ retry_add_item(address_item *addr, uschar *key, int flags) { retry_item *rti = store_get(sizeof(retry_item)); +host_item * host = addr->host_used; rti->next = addr->retries; addr->retries = rti; rti->key = key; rti->basic_errno = addr->basic_errno; rti->more_errno = addr->more_errno; -rti->message = addr->message; +rti->message = host + ? string_sprintf("H=%s [%s]: %s", host->name, host->address, addr->message) + : addr->message; rti->flags = flags; DEBUG(D_transport|D_retry) @@ -340,12 +343,10 @@ */ retry_config * -retry_find_config(uschar *key, uschar *alternate, int basic_errno, +retry_find_config(const uschar *key, const uschar *alternate, int basic_errno, int more_errno) { -int replace = 0; -uschar *use_key, *use_alternate; -uschar *colon = Ustrchr(key, ':'); +const uschar *colon = Ustrchr(key, ':'); retry_config *yield; /* If there's a colon in the key, there are two possibilities: @@ -354,8 +355,7 @@ hostname:ip+port - In this case, we temporarily replace the colon with a zero, to terminate - the string after the host name. + In this case, we copy the host name. (2) This is a key for a pipe, file, or autoreply delivery, in the format @@ -366,28 +366,22 @@ with a letter or a digit. In this case we want to use the original address to search for a retry rule. */ -if (colon != NULL) - { - if (isalnum(*key)) - replace = ':'; - else - key = Ustrrchr(key, ':') + 1; /* Take from the last colon */ - } - -if (replace == 0) colon = key + Ustrlen(key); -*colon = 0; +if (colon) + key = isalnum(*key) + ? string_copyn(key, colon-key) /* the hostname */ + : Ustrrchr(key, ':') + 1; /* Take from the last colon */ /* Sort out the keys */ -use_key = (Ustrchr(key, '@') != NULL)? key : string_sprintf("*@%s", key); -use_alternate = (alternate == NULL)? NULL : string_sprintf("*@%s", alternate); +if (!Ustrchr(key, '@')) key = string_sprintf("*@%s", key); +if (alternate) alternate = string_sprintf("*@%s", alternate); /* Scan the configured retry items. */ for (yield = retries; yield != NULL; yield = yield->next) { - uschar *plist = yield->pattern; - uschar *slist = yield->senders; + const uschar *plist = yield->pattern; + const uschar *slist = yield->senders; /* If a specific error is set for this item, check that we are handling that specific error, and if so, check any additional error information if @@ -486,15 +480,14 @@ /* Check for a match between the address list item at the start of this retry rule and either the main or alternate keys. */ - if (match_address_list(use_key, TRUE, TRUE, &plist, NULL, -1, UCHAR_MAX+1, + if (match_address_list(key, TRUE, TRUE, &plist, NULL, -1, UCHAR_MAX+1, NULL) == OK || - (use_alternate != NULL && - match_address_list(use_alternate, TRUE, TRUE, &plist, NULL, -1, + (alternate != NULL && + match_address_list(alternate, TRUE, TRUE, &plist, NULL, -1, UCHAR_MAX+1, NULL) == OK)) break; } -*colon = replace; return yield; } @@ -662,7 +655,7 @@ message = (rti->basic_errno > 0)? US strerror(rti->basic_errno) : (rti->message == NULL)? - US"unknown error" : string_printing(rti->message); + US"unknown error" : US string_printing(rti->message); message_length = Ustrlen(message); if (message_length > 150) message_length = 150; diff -Nru exim4-4.84/src/rewrite.c exim4-4.86~RC4/src/rewrite.c --- exim4-4.84/src/rewrite.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/rewrite.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with rewriting headers */ @@ -118,7 +118,8 @@ { int start, end, pdomain; int count = 0; - uschar *save_localpart, *save_domain; + uschar *save_localpart; + const uschar *save_domain; uschar *error, *new, *newparsed; /* Ensure that the flag matches the flags in the rule. */ @@ -162,7 +163,7 @@ /* Use the general function for matching an address against a list (here just one item, so use the "impossible value" separator UCHAR_MAX+1). */ - if (match_address_list(subject, FALSE, TRUE, &(rule->key), NULL, 0, + if (match_address_list(subject, FALSE, TRUE, CUSS &(rule->key), NULL, 0, UCHAR_MAX + 1, NULL) != OK) continue; @@ -292,7 +293,7 @@ { uschar *p1 = new + start - 1; uschar *p2 = new + end + 1; - uschar *pf1, *pf2; + const uschar *pf1, *pf2; uschar buff1[256], buff2[256]; while (p1 > new && p1[-1] == ' ') p1--; @@ -449,8 +450,9 @@ */ static header_line * -rewrite_one_header(header_line *h, int flag, uschar *routed_old, - uschar *routed_new, rewrite_rule *rewrite_rules, int existflags, BOOL replace) +rewrite_one_header(header_line *h, int flag, + const uschar *routed_old, const uschar *routed_new, + rewrite_rule *rewrite_rules, int existflags, BOOL replace) { int lastnewline = 0; header_line *newh = NULL; @@ -717,7 +719,8 @@ */ header_line * -rewrite_header(header_line *h, uschar *routed_old, uschar *routed_new, +rewrite_header(header_line *h, + const uschar *routed_old, const uschar *routed_new, rewrite_rule *rewrite_rules, int existflags, BOOL replace) { switch (h->type) diff -Nru exim4-4.84/src/route.c exim4-4.86~RC4/src/route.c --- exim4-4.84/src/route.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/route.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with routing, and the list of generic router options. */ @@ -54,14 +54,16 @@ (void *)offsetof(router_instance, debug_string) }, { "disable_logging", opt_bool | opt_public, (void *)offsetof(router_instance, disable_logging) }, + { "dnssec_request_domains", opt_stringptr|opt_public, + (void *)offsetof(router_instance, dnssec.request) }, + { "dnssec_require_domains", opt_stringptr|opt_public, + (void *)offsetof(router_instance, dnssec.require) }, { "domains", opt_stringptr|opt_public, (void *)offsetof(router_instance, domains) }, { "driver", opt_stringptr|opt_public, (void *)offsetof(router_instance, driver_name) }, - #ifdef EXPERIMENTAL_DSN { "dsn_lasthop", opt_bool|opt_public, (void *)offsetof(router_instance, dsn_lasthop) }, - #endif { "errors_to", opt_stringptr|opt_public, (void *)(offsetof(router_instance, errors_to)) }, { "expn", opt_bool|opt_public, @@ -275,14 +277,12 @@ if (r->pass_router_name != NULL) set_router(r, r->pass_router_name, &(r->pass_router), TRUE); - #ifdef EXPERIMENTAL_DSN - DEBUG(D_route) { + DEBUG(D_route) { if (r->dsn_lasthop == FALSE) debug_printf("DSN: %s propagating DSN\n", r->name); else debug_printf("DSN: %s lasthop set\n", r->name); } - #endif } } @@ -323,11 +323,11 @@ */ int -route_check_prefix(uschar *local_part, uschar *prefixes) +route_check_prefix(const uschar *local_part, const uschar *prefixes) { int sep = 0; uschar *prefix; -uschar *listptr = prefixes; +const uschar *listptr = prefixes; uschar prebuf[64]; while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf))) @@ -336,7 +336,7 @@ int plen = Ustrlen(prefix); if (prefix[0] == '*') { - uschar *p; + const uschar *p; prefix++; for (p = local_part + Ustrlen(local_part) - (--plen); p >= local_part; p--) @@ -367,12 +367,12 @@ */ int -route_check_suffix(uschar *local_part, uschar *suffixes) +route_check_suffix(const uschar *local_part, const uschar *suffixes) { int sep = 0; int alen = Ustrlen(local_part); uschar *suffix; -uschar *listptr = suffixes; +const uschar *listptr = suffixes; uschar sufbuf[64]; while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf))) @@ -381,7 +381,7 @@ int slen = Ustrlen(suffix); if (suffix[slen-1] == '*') { - uschar *p, *pend; + const uschar *p, *pend; pend = local_part + alen - (--slen) + 1; for (p = local_part; p < pend; p++) if (strncmpic(suffix, p, slen) == 0) return alen - (p - local_part); @@ -424,9 +424,9 @@ */ static int -route_check_dls(uschar *rname, uschar *type, uschar *list, tree_node - **anchorptr, unsigned int *cache_bits, int listtype, uschar *domloc, - uschar **ldata, BOOL caseless, uschar **perror) +route_check_dls(uschar *rname, uschar *type, const uschar *list, + tree_node **anchorptr, unsigned int *cache_bits, int listtype, + const uschar *domloc, const uschar **ldata, BOOL caseless, uschar **perror) { int rc; @@ -446,7 +446,7 @@ { uschar *address = (sender_address == NULL)? US"" : sender_address; rc = match_address_list(address, TRUE, TRUE, &list, cache_bits, -1, 0, - &sender_data); + CUSS &sender_data); } switch(rc) @@ -574,14 +574,15 @@ SKIP otherwise */ -int -check_files(uschar *s, uschar **perror) +static int +check_files(const uschar *s, uschar **perror) { int sep = 0; /* List has default separators */ uid_t uid = 0; /* For picky compilers */ gid_t gid = 0; /* For picky compilers */ BOOL ugid_set = FALSE; -uschar *check, *listptr; +const uschar *listptr; +uschar *check; uschar buffer[1024]; if (s == NULL) return OK; @@ -589,8 +590,7 @@ DEBUG(D_route) debug_printf("checking require_files\n"); listptr = s; -while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) - != NULL) +while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) { int rc; int eacces_code = 0; @@ -889,8 +889,8 @@ /* Skip this router if there's a domain mismatch. */ if ((rc = route_check_dls(r->name, US"domains", r->domains, &domainlist_anchor, - addr->domain_cache, TRUE, addr->domain, &deliver_domain_data, MCL_DOMAIN, - perror)) != OK) + addr->domain_cache, TRUE, addr->domain, CUSS &deliver_domain_data, + MCL_DOMAIN, perror)) != OK) return rc; /* Skip this router if there's a local part mismatch. We want to pass over the @@ -917,8 +917,8 @@ if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts, &localpartlist_anchor, localpart_cache, MCL_LOCALPART, - check_local_part, &deliver_localpart_data, !r->caseful_local_part, - perror)) != OK) + check_local_part, CUSS &deliver_localpart_data, + !r->caseful_local_part, perror)) != OK) return rc; /* If the check_local_user option is set, check that the local_part is the @@ -1092,7 +1092,7 @@ static uschar lastshell[128]; BOOL -route_finduser(uschar *s, struct passwd **pw, uid_t *return_uid) +route_finduser(const uschar *s, struct passwd **pw, uid_t *return_uid) { BOOL cache_set = (Ustrcmp(lastname, s) == 0); @@ -1306,67 +1306,6 @@ /************************************************* -* Sort out "more" or "unseen" * -*************************************************/ - -/* These values are usually fixed boolean values, but they are permitted to be -expanded strings. - -Arguments: - addr address being routed - rname the router name - oname the option name - bvalue the router's boolean value - svalue the router's string value - rvalue where to put the returned value - -Returns: OK value placed in rvalue - DEFER expansion failed -*/ - -static int -exp_bool(address_item *addr, uschar *rname, uschar *oname, BOOL bvalue, - uschar *svalue, BOOL *rvalue) -{ -uschar *expanded; -if (svalue == NULL) { *rvalue = bvalue; return OK; } - -expanded = expand_string(svalue); -if (expanded == NULL) - { - if (expand_string_forcedfail) - { - DEBUG(D_route) debug_printf("expansion of \"%s\" forced failure\n", oname); - *rvalue = bvalue; - return OK; - } - addr->message = string_sprintf("failed to expand \"%s\" in %s router: %s", - oname, rname, expand_string_message); - DEBUG(D_route) debug_printf("%s\n", addr->message); - return DEFER; - } - -DEBUG(D_route) debug_printf("expansion of \"%s\" yields \"%s\"\n", oname, - expanded); - -if (strcmpic(expanded, US"true") == 0 || strcmpic(expanded, US"yes") == 0) - *rvalue = TRUE; -else if (strcmpic(expanded, US"false") == 0 || strcmpic(expanded, US"no") == 0) - *rvalue = FALSE; -else - { - addr->message = string_sprintf("\"%s\" is not a valid value for the " - "\"%s\" option in the %s router", expanded, oname, rname); - return DEFER; - } - -return OK; -} - - - - -/************************************************* * Handle an unseen routing * *************************************************/ @@ -1407,8 +1346,8 @@ *parent = *addr; parent->child_count = 2; -parent->p.errors_address = - (addr->parent == NULL)? NULL : addr->parent->p.errors_address; +parent->prop.errors_address = + (addr->parent == NULL)? NULL : addr->parent->prop.errors_address; /* The routed address gets a new parent. */ @@ -1419,16 +1358,14 @@ take the errors address from the unseen router. */ new->parent = parent; -new->p.errors_address = parent->p.errors_address; +new->prop.errors_address = parent->prop.errors_address; /* Copy the propagated flags and address_data from the original. */ copyflag(new, addr, af_propagate); -new->p.address_data = addr->p.address_data; -#ifdef EXPERIMENTAL_DSN +new->prop.address_data = addr->prop.address_data; new->dsn_flags = addr->dsn_flags; new->dsn_orcpt = addr->dsn_orcpt; -#endif /* As it has turned out, we haven't set headers_add or headers_remove for the @@ -1504,7 +1441,7 @@ int yield = OK; BOOL unseen; router_instance *r, *nextr; -uschar *old_domain = addr->domain; +const uschar *old_domain = addr->domain; HDEBUG(D_route) { @@ -1689,8 +1626,8 @@ /* Expand "more" if necessary; DEFER => an expansion failed */ - yield = exp_bool(addr, r->name, US"more", r->more, r->expand_more, - &more); + yield = exp_bool(addr, US"router", r->name, D_route, + US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; if (!more) @@ -1712,7 +1649,7 @@ goto ROUTE_EXIT; } } - addr->p.address_data = deliver_address_data; + addr->prop.address_data = deliver_address_data; } /* We are finally cleared for take-off with this router. Clear the the flag @@ -1736,7 +1673,6 @@ /* Run the router, and handle the consequences. */ -#ifdef EXPERIMENTAL_DSN /* ... but let us check on DSN before. If this should be the last hop for DSN set flag */ @@ -1745,7 +1681,6 @@ addr->dsn_flags |= rf_dsnlasthop; HDEBUG(D_route) debug_printf("DSN: last hop for %s\n", addr->address); } -#endif HDEBUG(D_route) debug_printf("calling %s router\n", r->name); @@ -1799,7 +1734,8 @@ { /* Expand "more" if necessary */ - yield = exp_bool(addr, r->name, US"more", r->more, r->expand_more, &more); + yield = exp_bool(addr, US"router", r->name, D_route, + US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; if (!more) @@ -1939,8 +1875,8 @@ /* See if this is an unseen routing; first expand the option if necessary. DEFER can be given if the expansion fails */ -yield = exp_bool(addr, r->name, US"unseen", r->unseen, r->expand_unseen, - &unseen); +yield = exp_bool(addr, US"router", r->name, D_route, + US"unseen", r->unseen, r->expand_unseen, &unseen); if (yield != OK) goto ROUTE_EXIT; /* Debugging output recording a successful routing */ @@ -1959,8 +1895,8 @@ debug_printf(" transport: %s\n", (addr->transport == NULL)? US"" : addr->transport->name); - if (addr->p.errors_address != NULL) - debug_printf(" errors to %s\n", addr->p.errors_address); + if (addr->prop.errors_address != NULL) + debug_printf(" errors to %s\n", addr->prop.errors_address); for (h = addr->host_list; h != NULL; h = h->next) { @@ -1969,7 +1905,7 @@ if (h->mx >= 0) debug_printf(" MX=%d", h->mx); else if (h->mx != MX_NONE) debug_printf(" rgroup=%d", h->mx); if (h->port != PORT_NONE) debug_printf(" port=%d", h->port); - /* if (h->dnssec != DS_UNK) debug_printf(" dnssec=%s", h->dnssec==DS_YES ? "yes" : "no"); */ + if (h->dnssec != DS_UNK) debug_printf(" dnssec=%s", h->dnssec==DS_YES ? "yes" : "no"); debug_printf("\n"); } } @@ -1984,24 +1920,23 @@ /* Unset the address expansions, and return the final result. */ ROUTE_EXIT: -if (yield == DEFER) { - if ( - ((Ustrstr(addr->message, "failed to expand") != NULL) || (Ustrstr(addr->message, "expansion of ") != NULL)) && - ( - Ustrstr(addr->message, "mysql") != NULL || - Ustrstr(addr->message, "pgsql") != NULL || +if ( yield == DEFER + && addr->message + && ( Ustrstr(addr->message, "failed to expand") != NULL + || Ustrstr(addr->message, "expansion of ") != NULL + ) + && ( Ustrstr(addr->message, "mysql") != NULL + || Ustrstr(addr->message, "pgsql") != NULL #ifdef EXPERIMENTAL_REDIS - Ustrstr(addr->message, "redis") != NULL || + || Ustrstr(addr->message, "redis") != NULL #endif - Ustrstr(addr->message, "sqlite") != NULL || - Ustrstr(addr->message, "ldap:") != NULL || - Ustrstr(addr->message, "ldapdn:") != NULL || - Ustrstr(addr->message, "ldapm:") != NULL - ) - ) { - addr->message = string_sprintf("Temporary internal error"); - } -} + || Ustrstr(addr->message, "sqlite") != NULL + || Ustrstr(addr->message, "ldap:") != NULL + || Ustrstr(addr->message, "ldapdn:") != NULL + || Ustrstr(addr->message, "ldapm:") != NULL + ) + ) + addr->message = string_sprintf("Temporary internal error"); deliver_set_expansions(NULL); router_name = NULL; diff -Nru exim4-4.84/src/routers/accept.c exim4-4.86~RC4/src/routers/accept.c --- exim4-4.84/src/routers/accept.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/accept.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -118,9 +118,9 @@ addr, rblock->name, NULL)) return DEFER; addr->transport = rblock->transport; -addr->p.errors_address = errors_to; -addr->p.extra_headers = extra_headers; -addr->p.remove_headers = remove_headers; +addr->prop.errors_address = errors_to; +addr->prop.extra_headers = extra_headers; +addr->prop.remove_headers = remove_headers; return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? OK : DEFER; } diff -Nru exim4-4.84/src/routers/dnslookup.c exim4-4.86~RC4/src/routers/dnslookup.c --- exim4-4.84/src/routers/dnslookup.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/dnslookup.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -18,10 +18,8 @@ (void *)(offsetof(dnslookup_router_options_block, check_secondary_mx)) }, { "check_srv", opt_stringptr, (void *)(offsetof(dnslookup_router_options_block, check_srv)) }, - { "dnssec_request_domains", opt_stringptr, - (void *)(offsetof(dnslookup_router_options_block, dnssec_request_domains)) }, - { "dnssec_require_domains", opt_stringptr, - (void *)(offsetof(dnslookup_router_options_block, dnssec_require_domains)) }, + { "fail_defer_domains", opt_stringptr, + (void *)(offsetof(dnslookup_router_options_block, fail_defer_domains)) }, { "mx_domains", opt_stringptr, (void *)(offsetof(dnslookup_router_options_block, mx_domains)) }, { "mx_fail_domains", opt_stringptr, @@ -58,8 +56,7 @@ NULL, /* mx_fail_domains */ NULL, /* srv_fail_domains */ NULL, /* check_srv */ - NULL, /* dnssec_request_domains */ - NULL /* dnssec_require_domains */ + NULL /* fail_defer_domains */ }; @@ -146,10 +143,10 @@ (dnslookup_router_options_block *)(rblock->options_block); uschar *srv_service = NULL; uschar *widen = NULL; -uschar *pre_widen = addr->domain; -uschar *post_widen = NULL; -uschar *fully_qualified_name; -uschar *listptr; +const uschar *pre_widen = addr->domain; +const uschar *post_widen = NULL; +const uschar *fully_qualified_name; +const uschar *listptr; uschar widen_buffer[256]; addr_new = addr_new; /* Keep picky compilers happy */ @@ -161,10 +158,10 @@ /* If an SRV check is required, expand the service name */ -if (ob->check_srv != NULL) +if (ob->check_srv) { - srv_service = expand_string(ob->check_srv); - if (srv_service == NULL && !expand_string_forcedfail) + if ( !(srv_service = expand_string(ob->check_srv)) + && !expand_string_forcedfail) { addr->message = string_sprintf("%s router: failed to expand \"%s\": %s", rblock->name, ob->check_srv, expand_string_message); @@ -195,8 +192,8 @@ suppression of widening for sender addresses is silent because it is the normal desirable behaviour. */ -if (ob->widen_domains != NULL && - (verify != v_sender || !ob->rewrite_headers || addr->parent != NULL)) +if ( ob->widen_domains + && (verify != v_sender || !ob->rewrite_headers || addr->parent)) { listptr = ob->widen_domains; widen = string_nextinlist(&listptr, &widen_sep, widen_buffer, @@ -220,12 +217,12 @@ int flags = whichrrs; BOOL removed = FALSE; - if (pre_widen != NULL) + if (pre_widen) { h.name = pre_widen; pre_widen = NULL; } - else if (widen != NULL) + else if (widen) { h.name = string_sprintf("%s.%s", addr->domain, widen); widen = string_nextinlist(&listptr, &widen_sep, widen_buffer, @@ -233,7 +230,7 @@ DEBUG(D_route) debug_printf("%s router widened %s to %s\n", rblock->name, addr->domain, h.name); } - else if (post_widen != NULL) + else if (post_widen) { h.name = post_widen; post_widen = NULL; @@ -260,16 +257,15 @@ subsequently. We use the same logic as that for widen_domains above to avoid requesting a header rewrite that cannot work. */ - if (verify != v_sender || !ob->rewrite_headers || addr->parent != NULL) + if (verify != v_sender || !ob->rewrite_headers || addr->parent) { if (ob->qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE; if (ob->search_parents) flags |= HOST_FIND_SEARCH_PARENTS; } - rc = host_find_bydns(&h, rblock->ignore_target_hosts, flags, srv_service, + rc = host_find_bydns(&h, CUS rblock->ignore_target_hosts, flags, srv_service, ob->srv_fail_domains, ob->mx_fail_domains, - ob->dnssec_request_domains, ob->dnssec_require_domains, - &fully_qualified_name, &removed); + &rblock->dnssec, &fully_qualified_name, &removed); if (removed) setflag(addr, af_local_host_removed); /* If host found with only address records, test for the domain's being in @@ -277,9 +273,9 @@ the option is historical. */ if ((rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) && h.mx < 0 && - ob->mx_domains != NULL) - { - switch(match_isinlist(fully_qualified_name, &(ob->mx_domains), 0, + ob->mx_domains) + switch(match_isinlist(fully_qualified_name, + CUSS &(ob->mx_domains), 0, &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) { case DEFER: @@ -291,7 +287,6 @@ rblock->name, fully_qualified_name); continue; } - } /* Deferral returns forthwith, and anything other than failure breaks the loop. */ @@ -328,6 +323,7 @@ addr->message = US"an MX or SRV record indicated no SMTP service"; else { + addr->basic_errno = ERRNO_UNKNOWNHOST; addr->message = US"all relevant MX records point to non-existent hosts"; if (!allow_mx_to_ip && string_is_ip_address(h.name, NULL) != 0) { @@ -339,6 +335,22 @@ addr->message); } } + if (ob->fail_defer_domains) + { + switch(match_isinlist(fully_qualified_name, + CUSS &ob->fail_defer_domains, 0, + &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL)) + { + case DEFER: + addr->message = US"lookup defer for fail_defer_domains"; + return DEFER; + + case OK: + DEBUG(D_route) debug_printf("%s router: matched fail_defer_domains\n", + rblock->name); + return DEFER; + } + } return DECLINE; } @@ -402,13 +414,13 @@ /* Set up the errors address, if any. */ -rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address); if (rc != OK) return rc; /* Set up the additional and removeable headers for this address. */ -rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers), - &(addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers, + &addr->prop.remove_headers); if (rc != OK) return rc; /* Get store in which to preserve the original host item, chained on @@ -430,3 +442,5 @@ } /* End of routers/dnslookup.c */ +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/routers/dnslookup.h exim4-4.86~RC4/src/routers/dnslookup.h --- exim4-4.84/src/routers/dnslookup.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/dnslookup.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Private structure for the private options. */ @@ -17,8 +17,7 @@ uschar *mx_fail_domains; uschar *srv_fail_domains; uschar *check_srv; - uschar *dnssec_request_domains; - uschar *dnssec_require_domains; + uschar *fail_defer_domains; } dnslookup_router_options_block; /* Data for reading the private options. */ diff -Nru exim4-4.84/src/routers/ipliteral.c exim4-4.86~RC4/src/routers/ipliteral.c --- exim4-4.84/src/routers/ipliteral.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/ipliteral.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -99,8 +99,8 @@ (ipliteral_router_options_block *)(rblock->options_block); */ host_item *h; -uschar *domain = addr->domain; -uschar *ip; +const uschar *domain = addr->domain; +const uschar *ip; int len = Ustrlen(domain); int rc, ipv; @@ -116,29 +116,23 @@ "(unnamed)". */ if (domain[0] != '[' || domain[len-1] != ']') return DECLINE; -domain[len-1] = 0; /* temporarily */ - -ip = domain + 1; +ip = string_copyn(domain+1, len-2); if (strncmpic(ip, US"IPV6:", 5) == 0 || strncmpic(ip, US"IPV4:", 5) == 0) ip += 5; ipv = string_is_ip_address(ip, NULL); if (ipv == 0 || (disable_ipv6 && ipv == 6)) - { - domain[len-1] = ']'; return DECLINE; - } /* It seems unlikely that ignore_target_hosts will be used with this router, but if it is set, it should probably work. */ -if (verify_check_this_host(&(rblock->ignore_target_hosts), NULL, domain, - ip, NULL) == OK) +if (verify_check_this_host(CUSS&rblock->ignore_target_hosts, + NULL, domain, ip, NULL) == OK) { DEBUG(D_route) debug_printf("%s is in ignore_target_hosts\n", ip); addr->message = US"IP literal host explicitly ignored"; - domain[len-1] = ']'; return DECLINE; } @@ -149,8 +143,7 @@ h->next = NULL; h->address = string_copy(ip); h->port = PORT_NONE; -domain[len-1] = ']'; /* restore */ -h->name = string_copy(domain); +h->name = domain; h->mx = MX_NONE; h->status = hstatus_unknown; h->why = hwhy_unknown; @@ -172,13 +165,13 @@ /* Set up the errors address, if any. */ -rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address); if (rc != OK) return rc; /* Set up the additional and removeable headers for this address. */ -rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers), - &(addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers, + &addr->prop.remove_headers); if (rc != OK) return rc; /* Fill in the transport, queue the address for local or remote delivery, and diff -Nru exim4-4.84/src/routers/iplookup.c exim4-4.86~RC4/src/routers/iplookup.c --- exim4-4.84/src/routers/iplookup.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/iplookup.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -143,7 +143,8 @@ { uschar *query = NULL; uschar *reply; -uschar *hostname, *reroute, *domain, *listptr; +uschar *hostname, *reroute, *domain; +const uschar *listptr; uschar host_buffer[256]; host_item *host = store_get(sizeof(host_item)); address_item *new_addr; @@ -206,6 +207,7 @@ host->address = host->name; else { +/*XXX might want dnssec request/require on an iplookup router? */ int rc = host_find_byname(host, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, TRUE); if (rc == HOST_FIND_FAILED || rc == HOST_FIND_AGAIN) continue; } @@ -376,7 +378,7 @@ new_addr->parent = addr; copyflag(new_addr, addr, af_propagate); -new_addr->p = addr->p; +new_addr->prop = addr->prop; if (addr->child_count == SHRT_MAX) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d " @@ -388,11 +390,11 @@ /* Set up the errors address, if any, and the additional and removeable headers for this new address. */ -rc = rf_get_errors_address(addr, rblock, verify, &(new_addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &new_addr->prop.errors_address); if (rc != OK) return rc; -rc = rf_get_munge_headers(addr, rblock, &(new_addr->p.extra_headers), - &(new_addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &new_addr->prop.extra_headers, + &new_addr->prop.remove_headers); if (rc != OK) return rc; return OK; diff -Nru exim4-4.84/src/routers/manualroute.c exim4-4.86~RC4/src/routers/manualroute.c --- exim4-4.84/src/routers/manualroute.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/manualroute.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -139,8 +139,8 @@ */ static BOOL -parse_route_item(uschar *s, uschar **domain, uschar **hostlist, - uschar **options) +parse_route_item(const uschar *s, const uschar **domain, const uschar **hostlist, + const uschar **options) { while (*s != 0 && isspace(*s)) s++; @@ -226,9 +226,11 @@ { int rc, lookup_type; uschar *route_item = NULL; -uschar *options = NULL; -uschar *hostlist = NULL; -uschar *domain, *newhostlist, *listptr; +const uschar *options = NULL; +const uschar *hostlist = NULL; +const uschar *domain; +uschar *newhostlist; +const uschar *listptr; manualroute_router_options_block *ob = (manualroute_router_options_block *)(rblock->options_block); transport_instance *transport = NULL; @@ -260,7 +262,7 @@ /* Check the current domain; if it matches, break the loop */ if ((rc = match_isinlist(addr->domain, &domain, UCHAR_MAX+1, - &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, &lookup_value)) == OK) + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, CUSS &lookup_value)) == OK) break; /* If there was a problem doing the check, defer */ @@ -318,28 +320,26 @@ while (*options != 0) { - int term; - uschar *s = options; + unsigned n; + const uschar *s = options; while (*options != 0 && !isspace(*options)) options++; - term = *options; - *options = 0; + n = options-s; - if (Ustrcmp(s, "randomize") == 0) randomize = TRUE; - else if (Ustrcmp(s, "no_randomize") == 0) randomize = FALSE; - else if (Ustrcmp(s, "byname") == 0) lookup_type = lk_byname; - else if (Ustrcmp(s, "bydns") == 0) lookup_type = lk_bydns; + if (Ustrncmp(s, "randomize", n) == 0) randomize = TRUE; + else if (Ustrncmp(s, "no_randomize", n) == 0) randomize = FALSE; + else if (Ustrncmp(s, "byname", n) == 0) lookup_type = lk_byname; + else if (Ustrncmp(s, "bydns", n) == 0) lookup_type = lk_bydns; else { transport_instance *t; for (t = transports; t != NULL; t = t->next) - { if (Ustrcmp(t->name, s) == 0) { transport = t; individual_transport_set = TRUE; break; } - } + if (t == NULL) { s = string_sprintf("unknown routing option or transport name \"%s\"", s); @@ -349,7 +349,7 @@ } } - if (term != 0) + if (*options) { options++; while (*options != 0 && isspace(*options)) options++; @@ -358,13 +358,13 @@ /* Set up the errors address, if any. */ -rc = rf_get_errors_address(addr, rblock, verify, &(addr->p.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr->prop.errors_address); if (rc != OK) return rc; /* Set up the additional and removeable headers for this address. */ -rc = rf_get_munge_headers(addr, rblock, &(addr->p.extra_headers), - &(addr->p.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr->prop.extra_headers, + &addr->prop.remove_headers); if (rc != OK) return rc; /* If an individual transport is not set, get the transport for this router, if diff -Nru exim4-4.84/src/routers/queryprogram.c exim4-4.86~RC4/src/routers/queryprogram.c --- exim4-4.84/src/routers/queryprogram.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/queryprogram.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -114,7 +114,7 @@ next->parent = addr; orflag(next, addr, af_propagate); - next->p = *addr_prop; + next->prop = *addr_prop; next->start_router = rblock->redirect_router; next->next = *addr_new; @@ -192,7 +192,7 @@ pid_t pid; struct passwd *upw = NULL; uschar buffer[1024]; -uschar **argvptr; +const uschar **argvptr; uschar *rword, *rdata, *s; address_item_propagated addr_prop; queryprogram_router_options_block *ob = @@ -216,11 +216,11 @@ addr_prop.address_data = deliver_address_data; -rc = rf_get_errors_address(addr, rblock, verify, &(addr_prop.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr_prop.errors_address); if (rc != OK) return rc; -rc = rf_get_munge_headers(addr, rblock, &(addr_prop.extra_headers), - &(addr_prop.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr_prop.extra_headers, + &addr_prop.remove_headers); if (rc != OK) return rc; /* Get the fixed or expanded uid under which the command is to run @@ -526,7 +526,7 @@ /* Put the errors address, extra headers, and address_data into this address */ -addr->p = addr_prop; +addr->prop = addr_prop; /* Queue the address for local or remote delivery. */ diff -Nru exim4-4.84/src/routers/redirect.c exim4-4.86~RC4/src/routers/redirect.c --- exim4-4.84/src/routers/redirect.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/redirect.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -277,11 +277,11 @@ int verify, address_item_propagated *addr_prop) { int frc = rf_get_errors_address(addr, rblock, verify, - &(addr_prop->errors_address)); + &addr_prop->errors_address); if (frc != OK) return frc; -addr->p.errors_address = addr_prop->errors_address; -return rf_get_munge_headers(addr, rblock, &(addr_prop->extra_headers), - &(addr_prop->remove_headers)); +addr->prop.errors_address = addr_prop->errors_address; +return rf_get_munge_headers(addr, rblock, &addr_prop->extra_headers, + &addr_prop->remove_headers); } @@ -329,7 +329,7 @@ { address_item *parent; address_item *next = generated; - uschar *errors_address = next->p.errors_address; + uschar *errors_address = next->prop.errors_address; generated = next->next; next->parent = addr; @@ -378,8 +378,8 @@ If so, we must take care to re-instate it when we copy in the propagated data so that it overrides any errors_to setting on the router. */ - next->p = *addr_prop; - if (errors_address != NULL) next->p.errors_address = errors_address; + next->prop = *addr_prop; + if (errors_address != NULL) next->prop.errors_address = errors_address; /* For pipes, files, and autoreplies, record this router as handling them, because they don't go through the routing process again. Then set up uid, @@ -451,13 +451,18 @@ } } +#ifdef EXPERIMENTAL_INTERNATIONAL + next->prop.utf8_msg = string_is_utf8(next->address) + || (sender_address && string_is_utf8(sender_address)); +#endif + DEBUG(D_route) { debug_printf("%s router generated %s\n %serrors_to=%s transport=%s\n", rblock->name, next->address, testflag(next, af_pfr)? "pipe, file, or autoreply\n " : "", - next->p.errors_address, + next->prop.errors_address, (next->transport == NULL)? US"NULL" : next->transport->name); if (testflag(next, af_uid_set)) @@ -470,6 +475,10 @@ else debug_printf("gid=unset "); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (next->prop.utf8_msg) debug_printf("utf8 "); +#endif + debug_printf("home=%s\n", next->home_dir); } } @@ -517,7 +526,7 @@ redirect_router_options_block *ob = (redirect_router_options_block *)(rblock->options_block); address_item *generated = NULL; -uschar *save_qualify_domain_recipient = qualify_domain_recipient; +const uschar *save_qualify_domain_recipient = qualify_domain_recipient; uschar *discarded = US"discarded"; address_item_propagated addr_prop; error_block *eblock = NULL; @@ -891,7 +900,7 @@ data that propagates. */ copyflag(next, addr, af_propagate); - next->p = addr_prop; + next->prop = addr_prop; DEBUG(D_route) debug_printf("%s router autogenerated %s\n%s%s%s", rblock->name, diff -Nru exim4-4.84/src/routers/rf_change_domain.c exim4-4.86~RC4/src/routers/rf_change_domain.c --- exim4-4.84/src/routers/rf_change_domain.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/rf_change_domain.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -32,7 +32,7 @@ */ void -rf_change_domain(address_item *addr, uschar *domain, BOOL rewrite, +rf_change_domain(address_item *addr, const uschar *domain, BOOL rewrite, address_item **addr_new) { address_item *parent = store_get(sizeof(address_item)); @@ -52,7 +52,7 @@ up the new fields. */ *addr = address_defaults; -addr->p = parent->p; +addr->prop = parent->prop; addr->address = address; addr->unique = string_copy(address); diff -Nru exim4-4.84/src/routers/rf_functions.h exim4-4.86~RC4/src/routers/rf_functions.h --- exim4-4.84/src/routers/rf_functions.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/rf_functions.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Header for the functions that are shared by the routers */ @@ -11,7 +11,7 @@ extern void rf_add_generated(router_instance *, address_item **, address_item *, address_item *, uschar *, header_line *, uschar *, ugid_block *, struct passwd *); -extern void rf_change_domain(address_item *, uschar *, BOOL, address_item **); +extern void rf_change_domain(address_item *, const uschar *, BOOL, address_item **); extern uschar *rf_expand_data(address_item *, uschar *, int *); extern int rf_get_errors_address(address_item *, router_instance *, BOOL, uschar **); diff -Nru exim4-4.84/src/routers/rf_get_errors_address.c exim4-4.86~RC4/src/routers/rf_get_errors_address.c --- exim4-4.84/src/routers/rf_get_errors_address.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/rf_get_errors_address.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -38,7 +38,7 @@ { uschar *s; -*errors_to = addr->p.errors_address; +*errors_to = addr->prop.errors_address; if (rblock->errors_to == NULL) return OK; s = expand_string(rblock->errors_to); @@ -84,8 +84,8 @@ BOOL save_address_test_mode = address_test_mode; int save1 = 0; int i; - uschar ***p; - uschar *address_expansions_save[ADDRESS_EXPANSIONS_COUNT]; + const uschar ***p; + const uschar *address_expansions_save[ADDRESS_EXPANSIONS_COUNT]; address_item *snew = deliver_make_addr(s, FALSE); if (sender_address != NULL) diff -Nru exim4-4.84/src/routers/rf_get_munge_headers.c exim4-4.86~RC4/src/routers/rf_get_munge_headers.c --- exim4-4.84/src/routers/rf_get_munge_headers.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/rf_get_munge_headers.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -32,11 +32,11 @@ header_line **extra_headers, uschar **remove_headers) { /* Default is to retain existing headers */ -*extra_headers = addr->p.extra_headers; +*extra_headers = addr->prop.extra_headers; if (rblock->extra_headers) { - uschar * list = rblock->extra_headers; + const uschar * list = rblock->extra_headers; int sep = '\n'; uschar * s; int slen; @@ -46,8 +46,9 @@ { if (!expand_string_forcedfail) { - addr->message = string_sprintf("%s router failed to expand \"%s\": %s", - rblock->name, rblock->extra_headers, expand_string_message); + addr->message = string_sprintf( + "%s router failed to expand add_headers item \"%s\": %s", + rblock->name, s, expand_string_message); return DEFER; } } @@ -82,23 +83,23 @@ } /* Default is to retain existing removes */ -*remove_headers = addr->p.remove_headers; +*remove_headers = addr->prop.remove_headers; /* Expand items from colon-sep list separately, then build new list */ if (rblock->remove_headers) { - uschar * list = rblock->remove_headers; + const uschar * list = rblock->remove_headers; int sep = ':'; uschar * s; - uschar buffer[128]; - while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) + while ((s = string_nextinlist(&list, &sep, NULL, 0))) if (!(s = expand_string(s))) { if (!expand_string_forcedfail) { - addr->message = string_sprintf("%s router failed to expand \"%s\": %s", - rblock->name, rblock->remove_headers, expand_string_message); + addr->message = string_sprintf( + "%s router failed to expand remove_headers item \"%s\": %s", + rblock->name, s, expand_string_message); return DEFER; } } @@ -109,4 +110,6 @@ return OK; } +/* vi: aw ai sw=4 +*/ /* End of rf_get_munge_headers.c */ diff -Nru exim4-4.84/src/routers/rf_lookup_hostlist.c exim4-4.86~RC4/src/routers/rf_lookup_hostlist.c --- exim4-4.84/src/routers/rf_lookup_hostlist.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/rf_lookup_hostlist.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -64,7 +64,7 @@ prev = NULL; for (h = addr->host_list; h != NULL; h = next_h) { - uschar *canonical_name; + const uschar *canonical_name; int rc, len, port; next_h = h->next; @@ -94,8 +94,7 @@ NULL, /* SRV service not relevant */ NULL, /* failing srv domains not relevant */ NULL, /* no special mx failing domains */ - NULL, /* no dnssec request XXX ? */ - NULL, /* no dnssec require XXX ? */ + &rblock->dnssec, /* dnssec request/require */ NULL, /* fully_qualified_name */ NULL); /* indicate local host removed */ } @@ -120,7 +119,7 @@ DEBUG(D_route|D_host_lookup) debug_printf("doing DNS lookup\n"); rc = host_find_bydns(h, ignore_target_hosts, HOST_FIND_BY_A, NULL, NULL, NULL, - NULL, NULL, /*XXX dnssec? */ + &rblock->dnssec, /* domains for request/require */ &canonical_name, &removed); if (rc == HOST_FOUND) { @@ -168,6 +167,7 @@ if (hff_code == hff_pass) return PASS; if (hff_code == hff_decline) return DECLINE; + addr->basic_errno = ERRNO_UNKNOWNHOST; addr->message = string_sprintf("lookup of host \"%s\" failed in %s router%s", h->name, rblock->name, diff -Nru exim4-4.84/src/routers/rf_queue_add.c exim4-4.86~RC4/src/routers/rf_queue_add.c --- exim4-4.84/src/routers/rf_queue_add.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/routers/rf_queue_add.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -36,8 +36,8 @@ rf_queue_add(address_item *addr, address_item **paddr_local, address_item **paddr_remote, router_instance *rblock, struct passwd *pw) { -addr->p.domain_data = deliver_domain_data; /* Save these values for */ -addr->p.localpart_data = deliver_localpart_data; /* use in the transport */ +addr->prop.domain_data = deliver_domain_data; /* Save these values for */ +addr->prop.localpart_data = deliver_localpart_data; /* use in the transport */ /* Handle a local transport */ @@ -95,9 +95,9 @@ debug_printf("queued for %s transport: local_part = %s\ndomain = %s\n" " errors_to=%s\n", (addr->transport == NULL)? US"" : addr->transport->name, - addr->local_part, addr->domain, addr->p.errors_address); - debug_printf(" domain_data=%s localpart_data=%s\n", addr->p.domain_data, - addr->p.localpart_data); + addr->local_part, addr->domain, addr->prop.errors_address); + debug_printf(" domain_data=%s localpart_data=%s\n", addr->prop.domain_data, + addr->prop.localpart_data); } return TRUE; diff -Nru exim4-4.84/src/search.c exim4-4.86~RC4/src/search.c --- exim4-4.84/src/search.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/search.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* A set of functions to search databases in various formats. An open @@ -62,7 +62,7 @@ */ int -search_findtype(uschar *name, int len) +search_findtype(const uschar *name, int len) { int bot = 0; int top = lookup_list_count; @@ -121,12 +121,12 @@ */ int -search_findtype_partial(uschar *name, int *ptypeptr, uschar **ptypeaff, +search_findtype_partial(const uschar *name, int *ptypeptr, const uschar **ptypeaff, int *afflen, int *starflags) { int len, stype; int pv = -1; -uschar *ss = name; +const uschar *ss = name; *starflags = 0; *ptypeaff = NULL; @@ -601,7 +601,7 @@ uschar * search_find(void *handle, uschar *filename, uschar *keystring, int partial, - uschar *affix, int affixlen, int starflags, int *expand_setup) + const uschar *affix, int affixlen, int starflags, int *expand_setup) { tree_node *t = (tree_node *)handle; BOOL set_null_wild = FALSE; diff -Nru exim4-4.84/src/sieve.c exim4-4.86~RC4/src/sieve.c --- exim4-4.84/src/sieve.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/sieve.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Michael Haardt 2003-2008 */ +/* Copyright (c) Michael Haardt 2003 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This code was contributed by Michael Haardt. */ @@ -1072,7 +1072,7 @@ setflag(new_addr, af_pfr|af_file); new_addr->mode = 0; } -new_addr->p.errors_address = NULL; +new_addr->prop.errors_address = NULL; new_addr->next = *generated; *generated = new_addr; } @@ -2737,8 +2737,8 @@ 1 other success -1 syntax or execution error */ -static int parse_commands(struct Sieve *filter, int exec, - address_item **generated) +static int +parse_commands(struct Sieve *filter, int exec, address_item **generated) { while (*filter->pc) { @@ -2970,7 +2970,6 @@ int m; struct String from; struct String importance; - struct String *options; struct String message; struct String method; struct Notification *already; @@ -2991,7 +2990,6 @@ from.length=-1; importance.character=(uschar*)0; importance.length=-1; - options=(struct String*)0; message.character=(uschar*)0; message.length=-1; recipient=NULL; @@ -3362,7 +3360,8 @@ /* Allocation is larger than neccessary, but enough even for split MIME words */ buffer_capacity=32+4*subject.length; buffer=store_get(buffer_capacity); - addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE); + /* deconst cast safe as we pass in a non-const item */ + addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE); addr->reply->oncelog=once; addr->reply->once_repeat=days*86400; diff -Nru exim4-4.84/src/smtp_in.c exim4-4.86~RC4/src/smtp_in.c --- exim4-4.84/src/smtp_in.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/smtp_in.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -71,6 +71,7 @@ VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */ ETRN_CMD, /* This by analogy with TURN from the RFC */ STARTTLS_CMD, /* Required by the STARTTLS RFC */ + TLS_AUTH_CMD, /* auto-command at start of SSL */ /* This is a dummy to identify the non-sync commands when pipelining */ @@ -121,9 +122,7 @@ #ifdef SUPPORT_TLS static BOOL tls_advertised; #endif -#ifdef EXPERIMENTAL_DSN static BOOL dsn_advertised; -#endif static BOOL esmtp; static BOOL helo_required = FALSE; static BOOL helo_verify = FALSE; @@ -135,6 +134,9 @@ static BOOL rcpt_in_progress; static int nonmail_command_count; static BOOL smtp_exit_function_called = 0; +#ifdef EXPERIMENTAL_INTERNATIONAL +static BOOL smtputf8_advertised; +#endif static int synprot_error_count; static int unknown_command_count; static int sync_cmd_limit; @@ -160,12 +162,15 @@ count of non-mail commands and possibly provoke an error. */ static smtp_cmd_list cmd_list[] = { + /* name len cmd has_arg is_mail_cmd */ + { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE }, { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, #ifdef SUPPORT_TLS { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, + { "tls_auth", 0, TLS_AUTH_CMD, FALSE, TRUE }, #endif /* If you change anything above here, also fix the definitions below. */ @@ -189,6 +194,7 @@ #define CMD_LIST_EHLO 2 #define CMD_LIST_AUTH 3 #define CMD_LIST_STARTTLS 4 +#define CMD_LIST_TLS_AUTH 5 /* This list of names is used for performing the smtp_no_mail logging action. It must be kept in step with the SCH_xxx enumerations. */ @@ -199,7 +205,7 @@ US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", US"STARTTLS", US"VRFY" }; -static uschar *protocols[] = { +static uschar *protocols_local[] = { US"local-smtp", /* HELO */ US"local-smtps", /* The rare case EHLO->STARTTLS->HELO */ US"local-esmtp", /* EHLO */ @@ -207,12 +213,19 @@ US"local-esmtpa", /* EHLO->AUTH */ US"local-esmtpsa" /* EHLO->STARTTLS->EHLO->AUTH */ }; +static uschar *protocols[] = { + US"smtp", /* HELO */ + US"smtps", /* The rare case EHLO->STARTTLS->HELO */ + US"esmtp", /* EHLO */ + US"esmtps", /* EHLO->STARTTLS->EHLO */ + US"esmtpa", /* EHLO->AUTH */ + US"esmtpsa" /* EHLO->STARTTLS->EHLO->AUTH */ + }; #define pnormal 0 #define pextend 2 #define pcrpted 1 /* added to pextend or pnormal */ #define pauthed 2 /* added to pextend */ -#define pnlocal 6 /* offset to remove "local" */ /* Sanity check and validate optional args to MAIL FROM: envelope */ enum { @@ -220,8 +233,9 @@ #ifndef DISABLE_PRDR ENV_MAIL_OPT_PRDR, #endif -#ifdef EXPERIMENTAL_DSN ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID, +#ifdef EXPERIMENTAL_INTERNATIONAL + ENV_MAIL_OPT_UTF8, #endif ENV_MAIL_OPT_NULL }; @@ -238,9 +252,10 @@ #ifndef DISABLE_PRDR { US"PRDR", ENV_MAIL_OPT_PRDR, FALSE }, #endif -#ifdef EXPERIMENTAL_DSN { US"RET", ENV_MAIL_OPT_RET, TRUE }, { US"ENVID", ENV_MAIL_OPT_ENVID, TRUE }, +#ifdef EXPERIMENTAL_INTERNATIONAL + { US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */ #endif { US"NULL", ENV_MAIL_OPT_NULL, FALSE } }; @@ -1500,11 +1515,11 @@ memset(sender_address_cache, 0, sizeof(sender_address_cache)); memset(sender_domain_cache, 0, sizeof(sender_domain_cache)); -#ifdef EXPERIMENTAL_DSN +prdr_requested = FALSE; + /* Reset the DSN flags */ dsn_ret = 0; dsn_envid = NULL; -#endif authenticated_sender = NULL; #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -1522,6 +1537,9 @@ spf_result = NULL; spf_smtp_comment = NULL; #endif +#ifdef EXPERIMENTAL_INTERNATIONAL +message_smtputf8 = FALSE; +#endif body_linecount = body_zerocount = 0; sender_rate = sender_rate_limit = sender_rate_period = NULL; @@ -1630,6 +1648,7 @@ it is the canonical extracted address which is all that is kept. */ case MAIL_CMD: + smtp_mailcmd_count++; /* Count for no-mail log */ if (sender_address != NULL) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given"); @@ -1855,8 +1874,9 @@ tls_in.ocsp = OCSP_NOT_REQ; tls_advertised = FALSE; #endif -#ifdef EXPERIMENTAL_DSN dsn_advertised = FALSE; +#ifdef EXPERIMENTAL_INTERNATIONAL +smtputf8_advertised = FALSE; #endif /* Reset ACL connection variables */ @@ -1885,7 +1905,7 @@ else received_protocol = - protocols[pnormal] + ((sender_host_address != NULL)? pnlocal : 0); + (sender_host_address ? protocols : protocols_local) [pnormal]; /* Set up the buffer for inputting using direct read() calls, and arrange to call the local functions instead of the standard C ones. */ @@ -2139,6 +2159,19 @@ set_process_info("handling incoming connection from %s", host_and_ident(FALSE)); + /* Expand smtp_receive_timeout, if needed */ + + if (smtp_receive_timeout_s) + { + uschar * exp; + if ( !(exp = expand_string(smtp_receive_timeout_s)) + || !(*exp) + || (smtp_receive_timeout = readconf_readtime(exp, 0, FALSE)) < 0 + ) + log_write(0, LOG_MAIN|LOG_PANIC, + "bad value for smtp_receive_timeout: '%s'", exp ? exp : US""); + } + /* Start up TLS if tls_on_connect is set. This is for supporting the legacy smtps port for use with older style SSL MTAs. */ @@ -2435,7 +2468,7 @@ yield = 1; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "syntax or protocol errors (last command was \"%s\")", - host_and_ident(FALSE), smtp_cmd_buffer); + host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); } if (code > 0) @@ -2974,29 +3007,25 @@ /* If a host name is known, check it and all its aliases. */ - if (sender_host_name != NULL) - { - helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0; - - if (helo_verified) + if (sender_host_name) + if ((helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0)) { + sender_helo_dnssec = sender_host_dnssec; HDEBUG(D_receive) debug_printf("matched host name\n"); } else { uschar **aliases = sender_host_aliases; - while (*aliases != NULL) - { - helo_verified = strcmpic(*aliases++, sender_helo_name) == 0; - if (helo_verified) break; - } - HDEBUG(D_receive) - { - if (helo_verified) + while (*aliases) + if ((helo_verified = strcmpic(*aliases++, sender_helo_name) == 0)) + { + sender_helo_dnssec = sender_host_dnssec; + break; + } + + HDEBUG(D_receive) if (helo_verified) debug_printf("matched alias %s\n", *(--aliases)); - } } - } /* Final attempt: try a forward lookup of the helo name */ @@ -3004,29 +3033,34 @@ { int rc; host_item h; + dnssec_domains d; + host_item *hh; + h.name = sender_helo_name; h.address = NULL; h.mx = MX_NONE; h.next = NULL; + d.request = US"*"; + d.require = US""; + HDEBUG(D_receive) debug_printf("getting IP address for %s\n", sender_helo_name); - rc = host_find_byname(&h, NULL, 0, NULL, TRUE); + rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A, + NULL, NULL, NULL, &d, NULL, NULL); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) - { - host_item *hh = &h; - while (hh != NULL) - { + for (hh = &h; hh; hh = hh->next) if (Ustrcmp(hh->address, sender_host_address) == 0) { helo_verified = TRUE; + if (h.dnssec == DS_YES) sender_helo_dnssec = TRUE; HDEBUG(D_receive) - debug_printf("IP address for %s matches calling address\n", - sender_helo_name); + { + debug_printf("IP address for %s matches calling address\n" + "Forward DNS security status: %sverified\n", + sender_helo_name, sender_helo_dnssec ? "" : "un"); + } break; } - hh = hh->next; - } - } } } @@ -3063,6 +3097,113 @@ +static int +smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss) +{ +const uschar *set_id = NULL; +int rc, i; + +/* Run the checking code, passing the remainder of the command line as +data. Initials the $auth variables as empty. Initialize $0 empty and set +it as the only set numerical variable. The authenticator may set $auth +and also set other numeric variables. The $auth variables are preferred +nowadays; the numerical variables remain for backwards compatibility. + +Afterwards, have a go at expanding the set_id string, even if +authentication failed - for bad passwords it can be useful to log the +userid. On success, require set_id to expand and exist, and put it in +authenticated_id. Save this in permanent store, as the working store gets +reset at HELO, RSET, etc. */ + +for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; +expand_nmax = 0; +expand_nlength[0] = 0; /* $0 contains nothing */ + +rc = (au->info->servercode)(au, smtp_cmd_data); +if (au->set_id) set_id = expand_string(au->set_id); +expand_nmax = -1; /* Reset numeric variables */ +for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth */ + +/* The value of authenticated_id is stored in the spool file and printed in +log lines. It must not contain binary zeros or newline characters. In +normal use, it never will, but when playing around or testing, this error +can (did) happen. To guard against this, ensure that the id contains only +printing characters. */ + +if (set_id) set_id = string_printing(set_id); + +/* For the non-OK cases, set up additional logging data if set_id +is not empty. */ + +if (rc != OK) + set_id = set_id && *set_id + ? string_sprintf(" (set_id=%s)", set_id) : US""; + +/* Switch on the result */ + +switch(rc) + { + case OK: + if (!au->set_id || set_id) /* Complete success */ + { + if (set_id) authenticated_id = string_copy_malloc(set_id); + sender_host_authenticated = au->name; + authentication_failed = FALSE; + authenticated_fail_id = NULL; /* Impossible to already be set? */ + + received_protocol = + (sender_host_address ? protocols : protocols_local) + [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)]; + *s = *ss = US"235 Authentication succeeded"; + authenticated_by = au; + break; + } + + /* Authentication succeeded, but we failed to expand the set_id string. + Treat this as a temporary error. */ + + auth_defer_msg = expand_string_message; + /* Fall through */ + + case DEFER: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = string_sprintf("435 Unable to authenticate at present%s", + auth_defer_user_msg); + *ss = string_sprintf("435 Unable to authenticate at present%s: %s", + set_id, auth_defer_msg); + break; + + case BAD64: + *s = *ss = US"501 Invalid base64 data"; + break; + + case CANCELLED: + *s = *ss = US"501 Authentication cancelled"; + break; + + case UNEXPECTED: + *s = *ss = US"553 Initial data not expected"; + break; + + case FAIL: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = US"535 Incorrect authentication data"; + *ss = string_sprintf("535 Incorrect authentication data%s", set_id); + break; + + default: + if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + *s = US"435 Internal error"; + *ss = string_sprintf("435 Internal error%s: return %d from authentication " + "check", set_id, rc); + break; + } + +return rc; +} + + + /************************************************* * Initialize for SMTP incoming message * *************************************************/ @@ -3114,6 +3255,7 @@ cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; #ifdef SUPPORT_TLS cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; +cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; #endif /* Set the local signal handler for SIGTERM - it tries to end off tidily */ @@ -3129,7 +3271,7 @@ while (done <= 0) { - uschar **argv; + const uschar **argv; uschar *etrn_command; uschar *etrn_serialize_key; uschar *errmess; @@ -3137,7 +3279,6 @@ uschar *user_msg = NULL; uschar *recipient = NULL; uschar *hello = NULL; - uschar *set_id = NULL; uschar *s, *ss; BOOL was_rej_mail = FALSE; BOOL was_rcpt = FALSE; @@ -3147,9 +3288,42 @@ int ptr, size, rc; int c, i; auth_instance *au; -#ifdef EXPERIMENTAL_DSN uschar *orcpt = NULL; int flags; + +#if defined(SUPPORT_TLS) && defined(AUTH_TLS) + /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */ + if ( tls_in.active >= 0 + && tls_in.peercert + && tls_in.certificate_verified + && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd + ) + { + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE; + if (acl_smtp_auth) + { + rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); + if (rc != OK) + { + done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); + continue; + } + } + + for (au = auths; au; au = au->next) + if (strcmpic(US"tls", au->driver_name) == 0) + { + smtp_cmd_data = NULL; + + if ((c = smtp_in_auth(au, &s, &ss)) != OK) + log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", + au->name, host_and_ident(FALSE), ss); + else + DEBUG(D_auth) debug_printf("tls auth succeeded\n"); + + break; + } + } #endif switch(smtp_read_command(TRUE)) @@ -3179,13 +3353,13 @@ US"AUTH command used when not advertised"); break; } - if (sender_host_authenticated != NULL) + if (sender_host_authenticated) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"already authenticated"); break; } - if (sender_address != NULL) + if (sender_address) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"not permitted in mail transaction"); @@ -3194,7 +3368,7 @@ /* Check the ACL */ - if (acl_smtp_auth != NULL) + if (acl_smtp_auth) { rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, &user_msg, &log_msg); if (rc != OK) @@ -3231,121 +3405,23 @@ as a server and which has been advertised (unless, sigh, allow_auth_ unadvertised is set). */ - for (au = auths; au != NULL; au = au->next) - { + for (au = auths; au; au = au->next) if (strcmpic(s, au->public_name) == 0 && au->server && - (au->advertised || allow_auth_unadvertised)) break; - } + (au->advertised || allow_auth_unadvertised)) + break; - if (au == NULL) + if (au) { - done = synprot_error(L_smtp_protocol_error, 504, NULL, - string_sprintf("%s authentication mechanism not supported", s)); - break; - } - - /* Run the checking code, passing the remainder of the command line as - data. Initials the $auth variables as empty. Initialize $0 empty and set - it as the only set numerical variable. The authenticator may set $auth - and also set other numeric variables. The $auth variables are preferred - nowadays; the numerical variables remain for backwards compatibility. - - Afterwards, have a go at expanding the set_id string, even if - authentication failed - for bad passwords it can be useful to log the - userid. On success, require set_id to expand and exist, and put it in - authenticated_id. Save this in permanent store, as the working store gets - reset at HELO, RSET, etc. */ - - for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; - expand_nmax = 0; - expand_nlength[0] = 0; /* $0 contains nothing */ - - c = (au->info->servercode)(au, smtp_cmd_data); - if (au->set_id != NULL) set_id = expand_string(au->set_id); - expand_nmax = -1; /* Reset numeric variables */ - for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth */ - - /* The value of authenticated_id is stored in the spool file and printed in - log lines. It must not contain binary zeros or newline characters. In - normal use, it never will, but when playing around or testing, this error - can (did) happen. To guard against this, ensure that the id contains only - printing characters. */ - - if (set_id != NULL) set_id = string_printing(set_id); - - /* For the non-OK cases, set up additional logging data if set_id - is not empty. */ - - if (c != OK) - { - if (set_id != NULL && *set_id != 0) - set_id = string_sprintf(" (set_id=%s)", set_id); - else set_id = US""; - } - - /* Switch on the result */ - - switch(c) - { - case OK: - if (au->set_id == NULL || set_id != NULL) /* Complete success */ - { - if (set_id != NULL) authenticated_id = string_copy_malloc(set_id); - sender_host_authenticated = au->name; - authentication_failed = FALSE; - authenticated_fail_id = NULL; /* Impossible to already be set? */ - received_protocol = - protocols[pextend + pauthed + ((tls_in.active >= 0)? pcrpted:0)] + - ((sender_host_address != NULL)? pnlocal : 0); - s = ss = US"235 Authentication succeeded"; - authenticated_by = au; - break; - } - - /* Authentication succeeded, but we failed to expand the set_id string. - Treat this as a temporary error. */ - - auth_defer_msg = expand_string_message; - /* Fall through */ - - case DEFER: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = string_sprintf("435 Unable to authenticate at present%s", - auth_defer_user_msg); - ss = string_sprintf("435 Unable to authenticate at present%s: %s", - set_id, auth_defer_msg); - break; - - case BAD64: - s = ss = US"501 Invalid base64 data"; - break; - - case CANCELLED: - s = ss = US"501 Authentication cancelled"; - break; - - case UNEXPECTED: - s = ss = US"553 Initial data not expected"; - break; + c = smtp_in_auth(au, &s, &ss); - case FAIL: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = US"535 Incorrect authentication data"; - ss = string_sprintf("535 Incorrect authentication data%s", set_id); - break; - - default: - if (set_id != NULL) authenticated_fail_id = string_copy_malloc(set_id); - s = US"435 Internal error"; - ss = string_sprintf("435 Internal error%s: return %d from authentication " - "check", set_id, c); - break; + smtp_printf("%s\r\n", s); + if (c != OK) + log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", + au->name, host_and_ident(FALSE), ss); } - - smtp_printf("%s\r\n", s); - if (c != OK) - log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", - au->name, host_and_ident(FALSE), ss); + else + done = synprot_error(L_smtp_protocol_error, 504, NULL, + string_sprintf("%s authentication mechanism not supported", s)); break; /* AUTH_CMD */ @@ -3396,7 +3472,7 @@ { log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "syntax or protocol errors (last command was \"%s\")", - host_and_ident(FALSE), smtp_cmd_buffer); + host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); done = 1; } @@ -3423,7 +3499,7 @@ if (sender_host_name == NULL && (deliver_domain = sender_helo_name, /* set $domain */ - match_isinlist(sender_helo_name, &helo_lookup_domains, 0, + match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK) (void)host_name_lookup(); @@ -3441,7 +3517,7 @@ now obsolescent, since the verification can now be requested selectively at ACL time. */ - helo_verified = helo_verify_failed = FALSE; + helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE; if (helo_required || helo_verify) { BOOL tempfail = !smtp_verify_helo(); @@ -3492,12 +3568,13 @@ auth_advertised = FALSE; pipelining_advertised = FALSE; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_advertised = FALSE; - #endif - #ifdef EXPERIMENTAL_DSN +#endif dsn_advertised = FALSE; - #endif +#ifdef EXPERIMENTAL_INTERNATIONAL + smtputf8_advertised = FALSE; +#endif smtp_code = US"250 "; /* Default response code plus space*/ if (user_msg == NULL) @@ -3581,7 +3658,6 @@ s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11); } - #ifdef EXPERIMENTAL_DSN /* Advertise DSN support if configured to do so. */ if (verify_check_host(&dsn_advertise_hosts) != FAIL) { @@ -3589,7 +3665,6 @@ s = string_cat(s, &size, &ptr, US"-DSN\r\n", 6); dsn_advertised = TRUE; } - #endif /* Advertise ETRN if there's an ACL checking whether a host is permitted to issue it; a check is made when any host actually tries. */ @@ -3632,45 +3707,47 @@ letters, so output the names in upper case, though we actually recognize them in either case in the AUTH command. */ - if (auths != NULL) - { - if (verify_check_host(&auth_advertise_hosts) == OK) - { - auth_instance *au; - BOOL first = TRUE; - for (au = auths; au != NULL; au = au->next) - { - if (au->server && (au->advertise_condition == NULL || - expand_check_condition(au->advertise_condition, au->name, - US"authenticator"))) - { - int saveptr; - if (first) - { - s = string_cat(s, &size, &ptr, smtp_code, 3); - s = string_cat(s, &size, &ptr, US"-AUTH", 5); - first = FALSE; - auth_advertised = TRUE; - } - saveptr = ptr; - s = string_cat(s, &size, &ptr, US" ", 1); - s = string_cat(s, &size, &ptr, au->public_name, - Ustrlen(au->public_name)); - while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); - au->advertised = TRUE; - } - else au->advertised = FALSE; - } - if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2); - } - } + if ( auths +#if defined(SUPPORT_TLS) && defined(AUTH_TLS) + && !sender_host_authenticated +#endif + && verify_check_host(&auth_advertise_hosts) == OK + ) + { + auth_instance *au; + BOOL first = TRUE; + for (au = auths; au; au = au->next) + if (au->server && (au->advertise_condition == NULL || + expand_check_condition(au->advertise_condition, au->name, + US"authenticator"))) + { + int saveptr; + if (first) + { + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-AUTH", 5); + first = FALSE; + auth_advertised = TRUE; + } + saveptr = ptr; + s = string_cat(s, &size, &ptr, US" ", 1); + s = string_cat(s, &size, &ptr, au->public_name, + Ustrlen(au->public_name)); + while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); + au->advertised = TRUE; + } + else + au->advertised = FALSE; + + if (!first) s = string_cat(s, &size, &ptr, US"\r\n", 2); + } /* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer) if it has been included in the binary, and the host matches tls_advertise_hosts. We must *not* advertise if we are already in a secure connection. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_in.active < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) { @@ -3678,16 +3755,26 @@ s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11); tls_advertised = TRUE; } - #endif +#endif - #ifndef DISABLE_PRDR +#ifndef DISABLE_PRDR /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ if (prdr_enable) { s = string_cat(s, &size, &ptr, smtp_code, 3); s = string_cat(s, &size, &ptr, US"-PRDR\r\n", 7); } - #endif +#endif + +#ifdef EXPERIMENTAL_INTERNATIONAL + if ( accept_8bitmime + && verify_check_host(&smtputf8_advertise_hosts) != FAIL) + { + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-SMTPUTF8\r\n", 11); + smtputf8_advertised = TRUE; + } +#endif /* Finish off the multiline reply with one that is always available. */ @@ -3700,9 +3787,9 @@ s[ptr] = 0; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr); else - #endif +#endif { int i = fwrite(s, 1, ptr, smtp_out); i = i; /* compiler quietening */ @@ -3717,16 +3804,13 @@ helo_seen = TRUE; /* Reset the protocol and the state, abandoning any previous message. */ - - received_protocol = (esmtp? - protocols[pextend + - ((sender_host_authenticated != NULL)? pauthed : 0) + - ((tls_in.active >= 0)? pcrpted : 0)] - : - protocols[pnormal + ((tls_in.active >= 0)? pcrpted : 0)]) - + - ((sender_host_address != NULL)? pnlocal : 0); - + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active >= 0 ? pcrpted : 0) + ]; smtp_reset(reset_point); toomany = FALSE; break; /* HELO/EHLO */ @@ -3799,10 +3883,8 @@ (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list); mail_args++ ) - { if (strcmpic(name, mail_args->name) == 0) break; - } if (mail_args->need_value && strcmpic(value, US"") == 0) break; @@ -3830,60 +3912,66 @@ and "7BIT" as body types, but take no action. */ case ENV_MAIL_OPT_BODY: if (accept_8bitmime) { - if (strcmpic(value, US"8BITMIME") == 0) { + if (strcmpic(value, US"8BITMIME") == 0) body_8bitmime = 8; - } else if (strcmpic(value, US"7BIT") == 0) { + else if (strcmpic(value, US"7BIT") == 0) body_8bitmime = 7; - } else { + else + { body_8bitmime = 0; done = synprot_error(L_smtp_syntax_error, 501, NULL, US"invalid data for BODY"); goto COMMAND_LOOP; - } + } DEBUG(D_receive) debug_printf("8BITMIME: %d\n", body_8bitmime); break; } arg_error = TRUE; break; - #ifdef EXPERIMENTAL_DSN - /* Handle the two DSN options, but only if configured to do so (which will have caused "DSN" to be given in the EHLO response). The code itself is included only if configured in at build time. */ case ENV_MAIL_OPT_RET: - if (dsn_advertised) { + if (dsn_advertised) + { /* Check if RET has already been set */ - if (dsn_ret > 0) { + if (dsn_ret > 0) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"RET can be specified once only"); goto COMMAND_LOOP; - } - dsn_ret = (strcmpic(value, US"HDRS") == 0)? dsn_ret_hdrs : - (strcmpic(value, US"FULL") == 0)? dsn_ret_full : 0; + } + dsn_ret = strcmpic(value, US"HDRS") == 0 + ? dsn_ret_hdrs + : strcmpic(value, US"FULL") == 0 + ? dsn_ret_full + : 0; DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret); /* Check for invalid invalid value, and exit with error */ - if (dsn_ret == 0) { + if (dsn_ret == 0) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"Value for RET is invalid"); goto COMMAND_LOOP; - } - } + } + } break; case ENV_MAIL_OPT_ENVID: - if (dsn_advertised) { + if (dsn_advertised) + { /* Check if the dsn envid has been already set */ - if (dsn_envid != NULL) { + if (dsn_envid != NULL) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"ENVID can be specified once only"); goto COMMAND_LOOP; - } + } dsn_envid = string_copy(value); DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid); - } + } break; - #endif /* Handle the AUTH extension. If the value given is not "<>" and either the ACL says "yes" or there is no ACL but the sending host is @@ -3922,34 +4010,34 @@ switch (rc) { case OK: - if (authenticated_by == NULL || - authenticated_by->mail_auth_condition == NULL || - expand_check_condition(authenticated_by->mail_auth_condition, - authenticated_by->name, US"authenticator")) - break; /* Accept the AUTH */ - - ignore_msg = US"server_mail_auth_condition failed"; - if (authenticated_id != NULL) - ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"", - ignore_msg, authenticated_id); + if (authenticated_by == NULL || + authenticated_by->mail_auth_condition == NULL || + expand_check_condition(authenticated_by->mail_auth_condition, + authenticated_by->name, US"authenticator")) + break; /* Accept the AUTH */ + + ignore_msg = US"server_mail_auth_condition failed"; + if (authenticated_id != NULL) + ignore_msg = string_sprintf("%s: authenticated ID=\"%s\"", + ignore_msg, authenticated_id); /* Fall through */ case FAIL: - authenticated_sender = NULL; - log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)", - value, host_and_ident(TRUE), ignore_msg); - break; + authenticated_sender = NULL; + log_write(0, LOG_MAIN, "ignoring AUTH=%s from %s (%s)", + value, host_and_ident(TRUE), ignore_msg); + break; /* Should only get DEFER or ERROR here. Put back terminator overrides for error message */ default: - value[-1] = '='; - name[-1] = ' '; - (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg, - log_msg); - goto COMMAND_LOOP; + value[-1] = '='; + name[-1] = ' '; + (void)smtp_handle_acl_fail(ACL_WHERE_MAILAUTH, rc, user_msg, + log_msg); + goto COMMAND_LOOP; } } break; @@ -3961,6 +4049,16 @@ break; #endif +#ifdef EXPERIMENTAL_INTERNATIONAL + case ENV_MAIL_OPT_UTF8: + if (smtputf8_advertised) + { + DEBUG(D_receive) debug_printf("smtputf8 requested\n"); + message_smtputf8 = allow_utf8_domains = TRUE; + received_protocol = string_sprintf("utf8%s", received_protocol); + } + break; +#endif /* Unknown option. Stick back the terminator characters and break the loop. Do the name-terminator second as extract_option sets value==name when it found no equal-sign. @@ -3993,9 +4091,10 @@ /* Now extract the address, first applying any SMTP-time rewriting. The TRUE flag allows "<>" as a sender address. */ - raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)? - rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", - global_rewrite_rules) : smtp_cmd_data; + raw_sender = rewrite_existflags & rewrite_smtp + ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + global_rewrite_rules) + : smtp_cmd_data; /* rfc821_domains = TRUE; << no longer needed */ raw_sender = @@ -4161,26 +4260,23 @@ rcpt_fail_count++; break; } - - #ifdef EXPERIMENTAL_DSN + /* Set the DSN flags orcpt and dsn_flags from the session*/ orcpt = NULL; flags = 0; if (esmtp) for(;;) { - uschar *name, *value, *end; - int size; + uschar *name, *value; if (!extract_option(&name, &value)) - { break; - } if (dsn_advertised && strcmpic(name, US"ORCPT") == 0) { /* Check whether orcpt has been already set */ - if (orcpt != NULL) { + if (orcpt) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"ORCPT can be specified once only"); goto COMMAND_LOOP; @@ -4192,32 +4288,39 @@ else if (dsn_advertised && strcmpic(name, US"NOTIFY") == 0) { /* Check if the notify flags have been already set */ - if (flags > 0) { + if (flags > 0) + { synprot_error(L_smtp_syntax_error, 501, NULL, US"NOTIFY can be specified once only"); goto COMMAND_LOOP; } - if (strcmpic(value, US"NEVER") == 0) flags |= rf_notify_never; else + if (strcmpic(value, US"NEVER") == 0) + flags |= rf_notify_never; + else { uschar *p = value; while (*p != 0) { uschar *pp = p; while (*pp != 0 && *pp != ',') pp++; - if (*pp == ',') *pp++ = 0; - if (strcmpic(p, US"SUCCESS") == 0) { - DEBUG(D_receive) debug_printf("DSN: Setting notify success\n"); - flags |= rf_notify_success; + if (*pp == ',') *pp++ = 0; + if (strcmpic(p, US"SUCCESS") == 0) + { + DEBUG(D_receive) debug_printf("DSN: Setting notify success\n"); + flags |= rf_notify_success; } - else if (strcmpic(p, US"FAILURE") == 0) { - DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n"); - flags |= rf_notify_failure; + else if (strcmpic(p, US"FAILURE") == 0) + { + DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n"); + flags |= rf_notify_failure; } - else if (strcmpic(p, US"DELAY") == 0) { - DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n"); - flags |= rf_notify_delay; + else if (strcmpic(p, US"DELAY") == 0) + { + DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n"); + flags |= rf_notify_delay; } - else { + else + { /* Catch any strange values */ synprot_error(L_smtp_syntax_error, 501, NULL, US"Invalid value for NOTIFY parameter"); @@ -4240,7 +4343,6 @@ break; } } - #endif /* Apply SMTP rewriting then extract the working address. Don't allow "<>" as a recipient address */ @@ -4355,21 +4457,14 @@ if (user_msg == NULL) smtp_printf("250 Accepted\r\n"); else smtp_user_msg(US"250", user_msg); receive_add_recipient(recipient, -1); - - #ifdef EXPERIMENTAL_DSN + /* Set the dsn flags in the recipients_list */ - if (orcpt != NULL) - recipients_list[recipients_count-1].orcpt = orcpt; - else - recipients_list[recipients_count-1].orcpt = NULL; + recipients_list[recipients_count-1].orcpt = orcpt; + recipients_list[recipients_count-1].dsn_flags = flags; - if (flags != 0) - recipients_list[recipients_count-1].dsn_flags = flags; - else - recipients_list[recipients_count-1].dsn_flags = 0; - DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", recipients_list[recipients_count-1].orcpt, recipients_list[recipients_count-1].dsn_flags); - #endif - + DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", + recipients_list[recipients_count-1].orcpt, + recipients_list[recipients_count-1].dsn_flags); } /* The recipient was discarded */ @@ -4380,13 +4475,11 @@ else smtp_user_msg(US"250", user_msg); rcpt_fail_count++; discarded = TRUE; - log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: " + log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: " "discarded by %s ACL%s%s", host_and_ident(TRUE), - (sender_address_unrewritten != NULL)? - sender_address_unrewritten : sender_address, + sender_address_unrewritten? sender_address_unrewritten : sender_address, smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT", - (log_msg == NULL)? US"" : US": ", - (log_msg == NULL)? US"" : log_msg); + log_msg ? US": " : US"", log_msg ? log_msg : US""); } /* Either the ACL failed the address, or it was deferred. */ @@ -4454,7 +4547,7 @@ ACL may have delayed. To handle cutthrough delivery enforce a dummy call to get the DATA command sent. */ - if (acl_smtp_predata == NULL && cutthrough_fd < 0) rc = OK; else + if (acl_smtp_predata == NULL && cutthrough.fd < 0) rc = OK; else { uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept"; enable_dollar_recipients = TRUE; @@ -4622,6 +4715,7 @@ helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE; + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; if (sender_helo_name != NULL) { store_free(sender_helo_name); @@ -4630,13 +4724,13 @@ set_process_info("handling incoming TLS connection from %s", host_and_ident(FALSE)); } - received_protocol = (esmtp? - protocols[pextend + pcrpted + - ((sender_host_authenticated != NULL)? pauthed : 0)] - : - protocols[pnormal + pcrpted]) - + - ((sender_host_address != NULL)? pnlocal : 0); + received_protocol = + (sender_host_address ? protocols : protocols_local) + [ (esmtp + ? pextend + (sender_host_authenticated ? pauthed : 0) + : pnormal) + + (tls_in.active >= 0 ? pcrpted : 0) + ]; sender_host_authenticated = NULL; authenticated_id = NULL; @@ -4856,7 +4950,7 @@ break; } etrn_command = US"exim -R"; - argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R", + argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R", smtp_cmd_data); } @@ -5021,7 +5115,7 @@ done = 2; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "unrecognized commands (last was \"%s\")", host_and_ident(FALSE), - smtp_cmd_buffer); + string_printing(smtp_cmd_buffer)); } else done = synprot_error(L_smtp_syntax_error, 500, NULL, diff -Nru exim4-4.84/src/smtp_out.c exim4-4.86~RC4/src/smtp_out.c --- exim4-4.84/src/smtp_out.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/smtp_out.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,13 +2,14 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* A number of functions for driving outgoing SMTP calls. */ #include "exim.h" +#include "transports/smtp.h" @@ -37,7 +38,7 @@ smtp_get_interface(uschar *istring, int host_af, address_item *addr, BOOL *changed, uschar **interface, uschar *msg) { -uschar *expint; +const uschar * expint; uschar *iface; int sep = 0; @@ -143,62 +144,25 @@ -/************************************************* -* Connect to remote host * -*************************************************/ - -/* Create a socket, and connect it to a remote host. IPv6 addresses are -detected by checking for a colon in the address. AF_INET6 is defined even on -non-IPv6 systems, to enable the code to be less messy. However, on such systems -host->address will always be an IPv4 address. - -The port field in the host item is used if it is set (usually router from SRV -records or elsewhere). In other cases, the default passed as an argument is -used, and the host item is updated with its value. - -Arguments: - host host item containing name and address (and sometimes port) - host_af AF_INET or AF_INET6 - port default remote port to connect to, in host byte order, for those - hosts whose port setting is PORT_NONE - interface outgoing interface address or NULL - timeout timeout value or 0 - keepalive TRUE to use keepalive - dscp DSCP value to assign to socket - -Returns: connected socket number, or -1 with errno set -*/ - int -smtp_connect(host_item *host, int host_af, int port, uschar *interface, - int timeout, BOOL keepalive, const uschar *dscp) +smtp_sock_connect(host_item * host, int host_af, int port, uschar * interface, + transport_instance * tb, int timeout) { -int on = 1; -int save_errno = 0; +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; +const uschar * dscp = ob->dscp; int dscp_value; int dscp_level; int dscp_option; int sock; +int on = 1; +int save_errno = 0; -if (host->port != PORT_NONE) - { - HDEBUG(D_transport|D_acl|D_v) - debug_printf("Transport port=%d replaced by host-specific port=%d\n", port, - host->port); - port = host->port; - } -else host->port = port; /* Set the port actually used */ - -HDEBUG(D_transport|D_acl|D_v) - { - if (interface == NULL) - debug_printf("Connecting to %s [%s]:%d ... ",host->name,host->address,port); - else - debug_printf("Connecting to %s [%s]:%d from %s ... ", host->name, - host->address, port, interface); - } - -/* Create the socket */ +#ifdef EXPERIMENTAL_EVENT +deliver_host_address = host->address; +deliver_host_port = port; +if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1; +#endif if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return -1; @@ -220,15 +184,13 @@ option for both; ignore failures here */ if (host_af == AF_INET6 && dscp_lookup(dscp, AF_INET, &dscp_level, &dscp_option, &dscp_value)) - { (void) setsockopt(sock, dscp_level, dscp_option, &dscp_value, sizeof(dscp_value)); - } } /* Bind to a specific interface if requested. Caller must ensure the interface is the same type (IPv4 or IPv6) as the outgoing address. */ -if (interface != NULL && ip_bind(sock, host_af, interface, 0) < 0) +if (interface && ip_bind(sock, host_af, interface, 0) < 0) { save_errno = errno; HDEBUG(D_transport|D_acl|D_v) @@ -274,11 +236,75 @@ close(sock); return -1; } - if (keepalive) ip_keepalive(sock, host->address, TRUE); + if (ob->keepalive) ip_keepalive(sock, host->address, TRUE); return sock; } } +/************************************************* +* Connect to remote host * +*************************************************/ + +/* Create a socket, and connect it to a remote host. IPv6 addresses are +detected by checking for a colon in the address. AF_INET6 is defined even on +non-IPv6 systems, to enable the code to be less messy. However, on such systems +host->address will always be an IPv4 address. + +The port field in the host item is used if it is set (usually router from SRV +records or elsewhere). In other cases, the default passed as an argument is +used, and the host item is updated with its value. + +Arguments: + host host item containing name and address (and sometimes port) + host_af AF_INET or AF_INET6 + port default remote port to connect to, in host byte order, for those + hosts whose port setting is PORT_NONE + interface outgoing interface address or NULL + timeout timeout value or 0 + tb transport + +Returns: connected socket number, or -1 with errno set +*/ + +int +smtp_connect(host_item *host, int host_af, int port, uschar *interface, + int timeout, transport_instance * tb) +{ +#ifdef EXPERIMENTAL_SOCKS +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tb->options_block; +#endif + +if (host->port != PORT_NONE) + { + HDEBUG(D_transport|D_acl|D_v) + debug_printf("Transport port=%d replaced by host-specific port=%d\n", port, + host->port); + port = host->port; + } +else host->port = port; /* Set the port actually used */ + +HDEBUG(D_transport|D_acl|D_v) + { + uschar * s = US" "; + if (interface) s = string_sprintf(" from %s ", interface); +#ifdef EXPERIMENTAL_SOCKS + if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s); +#endif + debug_printf("Connecting to %s [%s]:%d%s... ", + host->name, host->address, port, s); + } + +/* Create and connect the socket */ + +#ifdef EXPERIMENTAL_SOCKS +if (ob->socks_proxy) + return socks_sock_connect(host, host_af, port, interface, tb, timeout); +#endif + +return smtp_sock_connect(host, host_af, port, interface, tb, timeout); +} + /************************************************* * Flush outgoing command buffer * @@ -569,3 +595,5 @@ } /* End of smtp_out.c */ +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/spam.c exim4-4.86~RC4/src/spam.c --- exim4-4.84/src/spam.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/spam.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner 2003-???? */ +/* Copyright (c) Tom Kistner 2003 - 2015 */ /* License: GPL */ /* Code for calling spamassassin's spamd. Called from acl.c. */ @@ -14,432 +14,638 @@ uschar spam_score_buffer[16]; uschar spam_score_int_buffer[16]; uschar spam_bar_buffer[128]; +uschar spam_action_buffer[32]; uschar spam_report_buffer[32600]; uschar prev_user_name[128] = ""; int spam_ok = 0; int spam_rc = 0; uschar *prev_spamd_address_work = NULL; -int -spam(uschar **listptr) +static const uschar * loglabel = US"spam acl condition:"; + + +static int +spamd_param_init(spamd_address_container *spamd) { - int sep = 0; - uschar *list = *listptr; - uschar *user_name; - uschar user_name_buffer[128]; - unsigned long mbox_size; - FILE *mbox_file; - int spamd_sock = -1; - uschar spamd_buffer[32600]; - int i, j, offset, result; - uschar spamd_version[8]; - uschar spamd_score_char; - double spamd_threshold, spamd_score; - int spamd_report_offset; - uschar *p,*q; - int override = 0; - time_t start; - size_t read, wrote; - struct sockaddr_un server; -#ifndef NO_POLL_H - struct pollfd pollfd; -#else /* Patch posted by Erik ? for OS X */ - struct timeval select_tv; /* and applied by PH */ - fd_set select_fd; -#endif - uschar *spamd_address_work; +/* default spamd server weight, time and priority value */ +spamd->is_rspamd = FALSE; +spamd->is_failed = FALSE; +spamd->weight = SPAMD_WEIGHT; +spamd->timeout = SPAMD_TIMEOUT; +spamd->retry = 0; +spamd->priority = 1; +return 0; +} - /* stop compiler warning */ - result = 0; - /* find the username from the option list */ - if ((user_name = string_nextinlist(&list, &sep, - user_name_buffer, - sizeof(user_name_buffer))) == NULL) { - /* no username given, this means no scanning should be done */ - return FAIL; - }; - - /* if username is "0" or "false", do not scan */ - if ( (Ustrcmp(user_name,"0") == 0) || - (strcmpic(user_name,US"false") == 0) ) { - return FAIL; - }; - - /* if there is an additional option, check if it is "true" */ - if (strcmpic(list,US"true") == 0) { - /* in that case, always return true later */ - override = 1; - }; - - /* expand spamd_address if needed */ - if (*spamd_address == '$') { - spamd_address_work = expand_string(spamd_address); - if (spamd_address_work == NULL) { - log_write(0, LOG_MAIN|LOG_PANIC, - "spamassassin acl condition: spamd_address starts with $, but expansion failed: %s", expand_string_message); - return DEFER; +static int +spamd_param(const uschar * param, spamd_address_container * spamd) +{ +static int timesinceday = -1; +const uschar * s; +const uschar * name; + +/*XXX more clever parsing could discard embedded spaces? */ + +if (sscanf(CCS param, "pri=%u", &spamd->priority)) + return 0; /* OK */ + +if (sscanf(CCS param, "weight=%u", &spamd->weight)) + { + if (spamd->weight == 0) /* this server disabled: skip it */ + return 1; + return 0; /* OK */ + } + +if (Ustrncmp(param, "time=", 5) == 0) + { + unsigned int start_h = 0, start_m = 0, start_s = 0; + unsigned int end_h = 24, end_m = 0, end_s = 0; + unsigned int time_start, time_end; + const uschar * end_string; + + name = US"time"; + s = param+5; + if ((end_string = Ustrchr(s, '-'))) + { + end_string++; + if ( sscanf(CS end_string, "%u.%u.%u", &end_h, &end_m, &end_s) == 0 + || sscanf(CS s, "%u.%u.%u", &start_h, &start_m, &start_s) == 0 + ) + goto badval; } - } else - spamd_address_work = spamd_address; + goto badval; - /* check if previous spamd_address was expanded and has changed. dump cached results if so */ - if ( spam_ok && ( prev_spamd_address_work != NULL) && (Ustrcmp(prev_spamd_address_work, spamd_address_work) != 0)) { - spam_ok = 0; - } - - /* if we scanned for this username last time, just return */ - if ( spam_ok && ( Ustrcmp(prev_user_name, user_name) == 0 ) ) { - if (override) - return OK; - else - return spam_rc; - }; + if (timesinceday < 0) + { + time_t now = time(NULL); + struct tm *tmp = localtime(&now); + timesinceday = tmp->tm_hour*3600 + tmp->tm_min*60 + tmp->tm_sec; + } - /* make sure the eml mbox file is spooled up */ - mbox_file = spool_mbox(&mbox_size, NULL); + time_start = start_h*3600 + start_m*60 + start_s; + time_end = end_h*3600 + end_m*60 + end_s; - if (mbox_file == NULL) { - /* error while spooling */ - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: error while creating mbox spool file"); - return DEFER; - }; + if (timesinceday < time_start || timesinceday >= time_end) + return 1; /* skip spamd server */ - start = time(NULL); + return 0; /* OK */ + } - /* socket does not start with '/' -> network socket */ - if (*spamd_address_work != '/') { - int num_servers = 0; - int current_server; - uschar *address = NULL; - uschar *spamd_address_list_ptr = spamd_address_work; - uschar address_buffer[256]; - spamd_address_container * spamd_address_vector[32]; - - /* Check how many spamd servers we have - and register their addresses */ - while ((address = string_nextinlist(&spamd_address_list_ptr, &sep, - address_buffer, - sizeof(address_buffer))) != NULL) { - - /* Potential memory leak as we never free the store. */ - spamd_address_container *this_spamd = - (spamd_address_container *)store_get(sizeof(spamd_address_container)); - - /* grok spamd address and port */ - if( sscanf(CS address, "%s %u", this_spamd->tcp_addr, &(this_spamd->tcp_port)) != 2 ) { - log_write(0, LOG_MAIN, - "spam acl condition: warning - invalid spamd address: '%s'", address); - continue; - }; - - spamd_address_vector[num_servers] = this_spamd; - num_servers++; - if (num_servers > 31) - break; - }; +if (Ustrcmp(param, "variant=rspamd") == 0) + { + spamd->is_rspamd = TRUE; + return 0; + } - /* check if we have at least one server */ - if (!num_servers) { - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: no useable spamd server addresses in spamd_address configuration option."); - (void)fclose(mbox_file); - return DEFER; - }; - - while ( num_servers > 0 ) { - int i; - - /* Randomly pick a server to try */ - current_server = random_number( num_servers ); - - debug_printf("trying server %s, port %u\n", - spamd_address_vector[current_server]->tcp_addr, - spamd_address_vector[current_server]->tcp_port); - - /* contact a spamd */ - if ( (spamd_sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: error creating IP socket for spamd"); - (void)fclose(mbox_file); - return DEFER; - }; - - if (ip_connect( spamd_sock, - AF_INET, - spamd_address_vector[current_server]->tcp_addr, - spamd_address_vector[current_server]->tcp_port, - 5 ) > -1) { - /* connection OK */ - break; - }; +if (Ustrncmp(param, "tmo=", 4) == 0) + { + int sec = readconf_readtime((s = param+4), '\0', FALSE); + name = US"timeout"; + if (sec < 0) + goto badval; + spamd->timeout = sec; + return 0; + } - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: warning - spamd connection to %s, port %u failed: %s", - spamd_address_vector[current_server]->tcp_addr, - spamd_address_vector[current_server]->tcp_port, - strerror(errno)); +if (Ustrncmp(param, "retry=", 6) == 0) + { + int sec = readconf_readtime((s = param+6), '\0', FALSE); + name = US"retry"; + if (sec < 0) + goto badval; + spamd->retry = sec; + return 0; + } - (void)close(spamd_sock); +log_write(0, LOG_MAIN, "%s warning - invalid spamd parameter: '%s'", + loglabel, param); +return -1; /* syntax error */ - /* Remove the server from the list. XXX We should free the memory */ - num_servers--; - for( i = current_server; i < num_servers; i++ ) - spamd_address_vector[i] = spamd_address_vector[i+1]; - } +badval: + log_write(0, LOG_MAIN, + "%s warning - invalid spamd %s value: '%s'", loglabel, name, s); + return -1; /* syntax error */ +} - if ( num_servers == 0 ) { - log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: all spamd servers failed"); - (void)fclose(mbox_file); - return DEFER; - } - } - else { - /* open the local socket */ +static int +spamd_get_server(spamd_address_container ** spamds, int num_servers) +{ +unsigned int i; +spamd_address_container * sd; +long rnd, weights; +unsigned pri; +static BOOL srandomed = FALSE; + +/* seedup, if we have only 1 server */ +if (num_servers == 1) + return (spamds[0]->is_failed ? -1 : 0); + +/* init ranmod */ +if (!srandomed) + { + struct timeval tv; + gettimeofday(&tv, NULL); + srandom((unsigned int)(tv.tv_usec/1000)); + srandomed = TRUE; + } + +/* scan for highest pri */ +for (pri = 0, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority > pri) pri = sd->priority; + } + +/* get sum of weights */ +for (weights = 0, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority == pri) weights += sd->weight; + } +if (weights == 0) /* all servers failed */ + return -1; + +for (rnd = random() % weights, i = 0; i < num_servers; i++) + { + sd = spamds[i]; + if (!sd->is_failed && sd->priority == pri) + if ((rnd -= sd->weight) <= 0) + return i; + } + +log_write(0, LOG_MAIN|LOG_PANIC, + "%s unknown error (memory/cpu corruption?)", loglabel); +return -1; +} - if ((spamd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, - "malware acl condition: spamd: unable to acquire socket (%s)", - strerror(errno)); - (void)fclose(mbox_file); - return DEFER; - } - server.sun_family = AF_UNIX; - Ustrcpy(server.sun_path, spamd_address_work); +int +spam(const uschar **listptr) +{ +int sep = 0; +const uschar *list = *listptr; +uschar *user_name; +uschar user_name_buffer[128]; +unsigned long mbox_size; +FILE *mbox_file; +int spamd_sock = -1; +uschar spamd_buffer[32600]; +int i, j, offset, result; +uschar spamd_version[8]; +uschar spamd_short_result[8]; +uschar spamd_score_char; +double spamd_threshold, spamd_score, spamd_reject_score; +int spamd_report_offset; +uschar *p,*q; +int override = 0; +time_t start; +size_t read, wrote; +#ifndef NO_POLL_H +struct pollfd pollfd; +#else /* Patch posted by Erik ? for OS X */ +struct timeval select_tv; /* and applied by PH */ +fd_set select_fd; +#endif +uschar *spamd_address_work; +spamd_address_container * sd; - if (connect(spamd_sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, - "malware acl condition: spamd: unable to connect to UNIX socket %s (%s)", - spamd_address_work, strerror(errno) ); - (void)fclose(mbox_file); - (void)close(spamd_sock); - return DEFER; - } +/* stop compiler warning */ +result = 0; +/* find the username from the option list */ +if ((user_name = string_nextinlist(&list, &sep, + user_name_buffer, + sizeof(user_name_buffer))) == NULL) + { + /* no username given, this means no scanning should be done */ + return FAIL; + } + +/* if username is "0" or "false", do not scan */ +if ( (Ustrcmp(user_name,"0") == 0) || + (strcmpic(user_name,US"false") == 0) ) + return FAIL; + +/* if there is an additional option, check if it is "true" */ +if (strcmpic(list,US"true") == 0) + /* in that case, always return true later */ + override = 1; + +/* expand spamd_address if needed */ +if (*spamd_address == '$') + { + spamd_address_work = expand_string(spamd_address); + if (spamd_address_work == NULL) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s spamd_address starts with $, but expansion failed: %s", + loglabel, expand_string_message); + return DEFER; + } } +else + spamd_address_work = spamd_address; + +DEBUG(D_acl) debug_printf("spamd: addrlist '%s'\n", spamd_address_work); + +/* check if previous spamd_address was expanded and has changed. dump cached results if so */ +if ( spam_ok + && prev_spamd_address_work != NULL + && Ustrcmp(prev_spamd_address_work, spamd_address_work) != 0 + ) + spam_ok = 0; + +/* if we scanned for this username last time, just return */ +if (spam_ok && Ustrcmp(prev_user_name, user_name) == 0) + return override ? OK : spam_rc; + +/* make sure the eml mbox file is spooled up */ +mbox_file = spool_mbox(&mbox_size, NULL); + +if (mbox_file == NULL) + { + /* error while spooling */ + log_write(0, LOG_MAIN|LOG_PANIC, + "%s error while creating mbox spool file", loglabel); + return DEFER; + } + +start = time(NULL); + + { + int num_servers = 0; + int current_server; + uschar * address; + const uschar * spamd_address_list_ptr = spamd_address_work; + spamd_address_container * spamd_address_vector[32]; + + /* Check how many spamd servers we have + and register their addresses */ + sep = 0; /* default colon-sep */ + while ((address = string_nextinlist(&spamd_address_list_ptr, &sep, + NULL, 0)) != NULL) + { + const uschar * sublist; + int sublist_sep = -(int)' '; /* default space-sep */ + unsigned args; + uschar * s; + + DEBUG(D_acl) debug_printf("spamd: addr entry '%s'\n", address); + sd = (spamd_address_container *)store_get(sizeof(spamd_address_container)); + + for (sublist = address, args = 0, spamd_param_init(sd); + s = string_nextinlist(&sublist, &sublist_sep, NULL, 0); + args++ + ) + { + DEBUG(D_acl) debug_printf("spamd: addr parm '%s'\n", s); + switch (args) + { + case 0: sd->hostspec = s; + if (*s == '/') args++; /* local; no port */ + break; + case 1: sd->hostspec = string_sprintf("%s %s", sd->hostspec, s); + break; + default: spamd_param(s, sd); + break; + } + } + if (args < 2) + { + log_write(0, LOG_MAIN, + "%s warning - invalid spamd address: '%s'", loglabel, address); + continue; + } - if (spamd_sock == -1) { + spamd_address_vector[num_servers] = sd; + if (++num_servers > 31) + break; + } + + /* check if we have at least one server */ + if (!num_servers) + { log_write(0, LOG_MAIN|LOG_PANIC, - "programming fault, spamd_sock unexpectedly unset"); - (void)fclose(mbox_file); - (void)close(spamd_sock); - return DEFER; + "%s no useable spamd server addresses in spamd_address configuration option.", + loglabel); + goto defer; + } + + current_server = spamd_get_server(spamd_address_vector, num_servers); + sd = spamd_address_vector[current_server]; + for(;;) + { + uschar * errstr; + + DEBUG(D_acl) debug_printf("spamd: trying server %s\n", sd->hostspec); + + for (;;) + { + if ( (spamd_sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0 + || sd->retry <= 0 + ) + break; + DEBUG(D_acl) debug_printf("sspamd: erver %s: retry conn\n", sd->hostspec); + while (sd->retry > 0) sd->retry = sleep(sd->retry); + } + if (spamd_sock >= 0) + break; + + log_write(0, LOG_MAIN, "%s spamd: %s", loglabel, errstr); + sd->is_failed = TRUE; + + current_server = spamd_get_server(spamd_address_vector, num_servers); + if (current_server < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "%s all spamd servers failed", loglabel); + goto defer; + } + sd = spamd_address_vector[current_server]; + } } - /* now we are connected to spamd on spamd_sock */ +if (spamd_sock == -1) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "programming fault, spamd_sock unexpectedly unset"); + goto defer; + } + +(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK); +/* now we are connected to spamd on spamd_sock */ +if (sd->is_rspamd) + { /* rspamd variant */ + uschar *req_str; + const uschar * helo; + const uschar * fcrdns; + const uschar * authid; + + req_str = string_sprintf("CHECK RSPAMC/1.3\r\nContent-length: %lu\r\n" + "Queue-Id: %s\r\nFrom: <%s>\r\nRecipient-Number: %d\r\n", + mbox_size, message_id, sender_address, recipients_count); + for (i = 0; i < recipients_count; i ++) + req_str = string_sprintf("%sRcpt: <%s>\r\n", req_str, recipients_list[i].address); + if ((helo = expand_string(US"$sender_helo_name")) != NULL && *helo != '\0') + req_str = string_sprintf("%sHelo: %s\r\n", req_str, helo); + if ((fcrdns = expand_string(US"$sender_host_name")) != NULL && *fcrdns != '\0') + req_str = string_sprintf("%sHostname: %s\r\n", req_str, fcrdns); + if (sender_host_address != NULL) + req_str = string_sprintf("%sIP: %s\r\n", req_str, sender_host_address); + if ((authid = expand_string(US"$authenticated_id")) != NULL && *authid != '\0') + req_str = string_sprintf("%sUser: %s\r\n", req_str, authid); + req_str = string_sprintf("%s\r\n", req_str); + wrote = send(spamd_sock, req_str, Ustrlen(req_str), 0); + } +else + { /* spamassassin variant */ (void)string_format(spamd_buffer, - sizeof(spamd_buffer), - "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n", - user_name, - mbox_size); - + sizeof(spamd_buffer), + "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n", + user_name, + mbox_size); /* send our request */ - if (send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0) < 0) { - (void)close(spamd_sock); - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: spamd send failed: %s", strerror(errno)); - (void)fclose(mbox_file); - (void)close(spamd_sock); - return DEFER; - }; + wrote = send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0); + } + +if (wrote == -1) + { + (void)close(spamd_sock); + log_write(0, LOG_MAIN|LOG_PANIC, + "%s spamd send failed: %s", loglabel, strerror(errno)); + goto defer; + } - /* now send the file */ - /* spamd sometimes accepts conections but doesn't read data off - * the connection. We make the file descriptor non-blocking so - * that the write will only write sufficient data without blocking - * and we poll the desciptor to make sure that we can write without - * blocking. Short writes are gracefully handled and if the whole - * trasaction takes too long it is aborted. - * Note: poll() is not supported in OSX 10.2 and is reported to be - * broken in more recent versions (up to 10.4). - */ +/* now send the file */ +/* spamd sometimes accepts conections but doesn't read data off + * the connection. We make the file descriptor non-blocking so + * that the write will only write sufficient data without blocking + * and we poll the desciptor to make sure that we can write without + * blocking. Short writes are gracefully handled and if the whole + * trasaction takes too long it is aborted. + * Note: poll() is not supported in OSX 10.2 and is reported to be + * broken in more recent versions (up to 10.4). + */ #ifndef NO_POLL_H - pollfd.fd = spamd_sock; - pollfd.events = POLLOUT; +pollfd.fd = spamd_sock; +pollfd.events = POLLOUT; #endif - (void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK); - do { - read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file); - if (read > 0) { - offset = 0; +(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK); +do + { + read = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file); + if (read > 0) + { + offset = 0; again: #ifndef NO_POLL_H - result = poll(&pollfd, 1, 1000); + result = poll(&pollfd, 1, 1000); /* Patch posted by Erik ? for OS X and applied by PH */ #else - select_tv.tv_sec = 1; - select_tv.tv_usec = 0; - FD_ZERO(&select_fd); - FD_SET(spamd_sock, &select_fd); - result = select(spamd_sock+1, NULL, &select_fd, NULL, &select_tv); + select_tv.tv_sec = 1; + select_tv.tv_usec = 0; + FD_ZERO(&select_fd); + FD_SET(spamd_sock, &select_fd); + result = select(spamd_sock+1, NULL, &select_fd, NULL, &select_tv); #endif /* End Erik's patch */ - if (result == -1 && errno == EINTR) - goto again; - else if (result < 1) { - if (result == -1) - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: %s on spamd socket", strerror(errno)); - else { - if (time(NULL) - start < SPAMD_TIMEOUT) - goto again; - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: timed out writing spamd socket"); - } - (void)close(spamd_sock); - (void)fclose(mbox_file); - return DEFER; + if (result == -1 && errno == EINTR) + goto again; + else if (result < 1) + { + if (result == -1) + log_write(0, LOG_MAIN|LOG_PANIC, + "%s %s on spamd socket", loglabel, strerror(errno)); + else + { + if (time(NULL) - start < sd->timeout) + goto again; + log_write(0, LOG_MAIN|LOG_PANIC, + "%s timed out writing spamd socket", loglabel); + } + (void)close(spamd_sock); + goto defer; } - wrote = send(spamd_sock,spamd_buffer + offset,read - offset,0); - if (wrote == -1) + wrote = send(spamd_sock,spamd_buffer + offset,read - offset,0); + if (wrote == -1) { - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: %s on spamd socket", strerror(errno)); - (void)close(spamd_sock); - (void)fclose(mbox_file); - return DEFER; + log_write(0, LOG_MAIN|LOG_PANIC, + "%s %s on spamd socket", loglabel, strerror(errno)); + (void)close(spamd_sock); + goto defer; } - if (offset + wrote != read) { - offset += wrote; - goto again; + if (offset + wrote != read) + { + offset += wrote; + goto again; } } } - while (!feof(mbox_file) && !ferror(mbox_file)); - if (ferror(mbox_file)) { - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: error reading spool file: %s", strerror(errno)); - (void)close(spamd_sock); - (void)fclose(mbox_file); - return DEFER; +while (!feof(mbox_file) && !ferror(mbox_file)); + +if (ferror(mbox_file)) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s error reading spool file: %s", loglabel, strerror(errno)); + (void)close(spamd_sock); + goto defer; } - (void)fclose(mbox_file); +(void)fclose(mbox_file); - /* we're done sending, close socket for writing */ - shutdown(spamd_sock,SHUT_WR); +/* we're done sending, close socket for writing */ +shutdown(spamd_sock,SHUT_WR); - /* read spamd response using what's left of the timeout. - */ - memset(spamd_buffer, 0, sizeof(spamd_buffer)); - offset = 0; - while((i = ip_recv(spamd_sock, - spamd_buffer + offset, - sizeof(spamd_buffer) - offset - 1, - SPAMD_TIMEOUT - time(NULL) + start)) > 0 ) { - offset += i; +/* read spamd response using what's left of the timeout. */ +memset(spamd_buffer, 0, sizeof(spamd_buffer)); +offset = 0; +while ((i = ip_recv(spamd_sock, + spamd_buffer + offset, + sizeof(spamd_buffer) - offset - 1, + sd->timeout - time(NULL) + start)) > 0 ) + offset += i; + +/* error handling */ +if (i <= 0 && errno != 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s error reading from spamd socket: %s", loglabel, strerror(errno)); + (void)close(spamd_sock); + return DEFER; } - /* error handling */ - if((i <= 0) && (errno != 0)) { +/* reading done */ +(void)close(spamd_sock); + +if (sd->is_rspamd) + { /* rspamd variant of reply */ + int r; + if ((r = sscanf(CS spamd_buffer, + "RSPAMD/%7s 0 EX_OK\r\nMetric: default; %7s %lf / %lf / %lf\r\n%n", + spamd_version, spamd_short_result, &spamd_score, &spamd_threshold, + &spamd_reject_score, &spamd_report_offset)) != 5) + { log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: error reading from spamd socket: %s", strerror(errno)); - (void)close(spamd_sock); + "%s cannot parse spamd output: %d", loglabel, r); return DEFER; - } + } + /* now parse action */ + p = &spamd_buffer[spamd_report_offset]; - /* reading done */ - (void)close(spamd_sock); + if (Ustrncmp(p, "Action: ", sizeof("Action: ") - 1) == 0) + { + p += sizeof("Action: ") - 1; + q = &spam_action_buffer[0]; + while (*p && *p != '\r' && (q - spam_action_buffer) < sizeof(spam_action_buffer) - 1) + *q++ = *p++; + *q = '\0'; + } + } +else + { /* spamassassin */ + /* dig in the spamd output and put the report in a multiline header, + if requested */ + if (sscanf(CS spamd_buffer, + "SPAMD/%7s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n", + spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3) + { + /* try to fall back to pre-2.50 spamd output */ + if (sscanf(CS spamd_buffer, + "SPAMD/%7s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n", + spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "%s cannot parse spamd output", loglabel); + return DEFER; + } + } - /* dig in the spamd output and put the report in a multiline header, if requested */ - if( sscanf(CS spamd_buffer,"SPAMD/%7s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n", - spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) { - - /* try to fall back to pre-2.50 spamd output */ - if( sscanf(CS spamd_buffer,"SPAMD/%7s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n", - spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) { - log_write(0, LOG_MAIN|LOG_PANIC, - "spam acl condition: cannot parse spamd output"); - return DEFER; - }; - }; + Ustrcpy(spam_action_buffer, + spamd_score >= spamd_threshold ? "reject" : "no action"); + } - /* Create report. Since this is a multiline string, - we must hack it into shape first */ - p = &spamd_buffer[spamd_report_offset]; - q = spam_report_buffer; - while (*p != '\0') { - /* skip \r */ - if (*p == '\r') { - p++; - continue; - }; - *q = *p; - q++; - if (*p == '\n') { - /* add an extra space after the newline to ensure - that it is treated as a header continuation line */ - *q = ' '; - q++; - }; +/* Create report. Since this is a multiline string, +we must hack it into shape first */ +p = &spamd_buffer[spamd_report_offset]; +q = spam_report_buffer; +while (*p != '\0') + { + /* skip \r */ + if (*p == '\r') + { p++; - }; - /* NULL-terminate */ - *q = '\0'; - q--; - /* cut off trailing leftovers */ - while (*q <= ' ') { - *q = '\0'; - q--; - }; - spam_report = spam_report_buffer; - - /* create spam bar */ - spamd_score_char = spamd_score > 0 ? '+' : '-'; - j = abs((int)(spamd_score)); - i = 0; - if( j != 0 ) { - while((i < j) && (i <= MAX_SPAM_BAR_CHARS)) - spam_bar_buffer[i++] = spamd_score_char; - } - else{ - spam_bar_buffer[0] = '/'; - i = 1; - } - spam_bar_buffer[i] = '\0'; - spam_bar = spam_bar_buffer; - - /* create "float" spam score */ - (void)string_format(spam_score_buffer, sizeof(spam_score_buffer),"%.1f", spamd_score); - spam_score = spam_score_buffer; - - /* create "int" spam score */ - j = (int)((spamd_score + 0.001)*10); - (void)string_format(spam_score_int_buffer, sizeof(spam_score_int_buffer), "%d", j); - spam_score_int = spam_score_int_buffer; - - /* compare threshold against score */ - if (spamd_score >= spamd_threshold) { - /* spam as determined by user's threshold */ - spam_rc = OK; - } - else { - /* not spam */ - spam_rc = FAIL; - }; - - /* remember expanded spamd_address if needed */ - if (spamd_address_work != spamd_address) { - prev_spamd_address_work = string_copy(spamd_address_work); - } - /* remember user name and "been here" for it */ - Ustrcpy(prev_user_name, user_name); - spam_ok = 1; - - if (override) { - /* always return OK, no matter what the score */ - return OK; - } - else { - return spam_rc; - }; + continue; + } + *q++ = *p; + if (*p++ == '\n') + { + /* add an extra space after the newline to ensure + that it is treated as a header continuation line */ + *q++ = ' '; + } + } +/* NULL-terminate */ +*q-- = '\0'; +/* cut off trailing leftovers */ +while (*q <= ' ') + *q-- = '\0'; + +spam_report = spam_report_buffer; +spam_action = spam_action_buffer; + +/* create spam bar */ +spamd_score_char = spamd_score > 0 ? '+' : '-'; +j = abs((int)(spamd_score)); +i = 0; +if (j != 0) + while ((i < j) && (i <= MAX_SPAM_BAR_CHARS)) + spam_bar_buffer[i++] = spamd_score_char; +else + { + spam_bar_buffer[0] = '/'; + i = 1; + } +spam_bar_buffer[i] = '\0'; +spam_bar = spam_bar_buffer; + +/* create "float" spam score */ +(void)string_format(spam_score_buffer, sizeof(spam_score_buffer), + "%.1f", spamd_score); +spam_score = spam_score_buffer; + +/* create "int" spam score */ +j = (int)((spamd_score + 0.001)*10); +(void)string_format(spam_score_int_buffer, sizeof(spam_score_int_buffer), + "%d", j); +spam_score_int = spam_score_int_buffer; + +/* compare threshold against score */ +spam_rc = spamd_score >= spamd_threshold + ? OK /* spam as determined by user's threshold */ + : FAIL; /* not spam */ + +/* remember expanded spamd_address if needed */ +if (spamd_address_work != spamd_address) + prev_spamd_address_work = string_copy(spamd_address_work); + +/* remember user name and "been here" for it */ +Ustrcpy(prev_user_name, user_name); +spam_ok = 1; + +return override + ? OK /* always return OK, no matter what the score */ + : spam_rc; + +defer: + (void)fclose(mbox_file); + return DEFER; } #endif +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/spam.h exim4-4.86~RC4/src/spam.h --- exim4-4.84/src/spam.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/spam.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner 2003-???? */ +/* Copyright (c) Tom Kistner 2003 - 2015 */ /* License: GPL */ /* spam defines */ @@ -17,12 +17,21 @@ /* SHUT_WR seems to be undefined on Unixware ? */ #ifndef SHUT_WR -#define SHUT_WR 1 +# define SHUT_WR 1 #endif -typedef struct spamd_address_container { - uschar tcp_addr[24]; - unsigned int tcp_port; +/* default weight */ +#define SPAMD_WEIGHT 1 + +typedef struct spamd_address_container +{ + uschar * hostspec; + int is_rspamd:1; + int is_failed:1; + unsigned int weight; + unsigned int timeout; + unsigned int retry; + unsigned int priority; } spamd_address_container; #endif diff -Nru exim4-4.84/src/spool_in.c exim4-4.86~RC4/src/spool_in.c --- exim4-4.84/src/spool_in.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/spool_in.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading spool files. When compiling for a utility (eximon), @@ -284,22 +284,32 @@ #ifdef SUPPORT_TLS tls_in.certificate_verified = FALSE; +# ifdef EXPERIMENTAL_DANE +tls_in.dane_verified = FALSE; +# endif tls_in.cipher = NULL; -tls_in.ourcert = NULL; -tls_in.peercert = NULL; +# ifndef COMPILE_UTILITY /* tls support fns not built in */ +tls_free_cert(&tls_in.ourcert); +tls_free_cert(&tls_in.peercert); +# endif tls_in.peerdn = NULL; tls_in.sni = NULL; tls_in.ocsp = OCSP_NOT_REQ; #endif #ifdef WITH_CONTENT_SCAN +spam_bar = NULL; +spam_score = NULL; spam_score_int = NULL; #endif -#ifdef EXPERIMENTAL_DSN +#if defined(EXPERIMENTAL_INTERNATIONAL) && !defined(COMPILE_UTILITY) +message_smtputf8 = FALSE; +message_utf8_downconvert = 0; +#endif + dsn_ret = 0; dsn_envid = NULL; -#endif /* Generate the full name and open the file. If message_subdir is already set, just look in the given directory. Otherwise, look in both the split @@ -357,6 +367,7 @@ originator_uid = (uid_t)uid; originator_gid = (gid_t)gid; +/* envelope from */ if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR; n = Ustrlen(big_buffer); if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>') @@ -366,6 +377,7 @@ Ustrncpy(sender_address, big_buffer+1, n-3); sender_address[n-3] = 0; +/* time */ if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR; if (sscanf(CS big_buffer, "%d %d", &received_time, &warning_count) != 2) goto SPOOL_FORMAT_ERROR; @@ -394,9 +406,22 @@ p = big_buffer + 2; for (;;) { + int len; if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR; if (big_buffer[0] != '-') break; - big_buffer[Ustrlen(big_buffer) - 1] = 0; + while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1 + && big_buffer[len-1] != '\n' + ) + { /* buffer not big enough for line; certs make this possible */ + uschar * buf; + if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR; + buf = store_get_perm(big_buffer_size *= 2); + memcpy(buf, big_buffer, --len); + big_buffer = buf; + if (Ufgets(big_buffer+len, big_buffer_size-len, f) == NULL) + goto SPOOL_READ_ERROR; + } + big_buffer[len-1] = 0; switch(big_buffer[1]) { @@ -466,33 +491,27 @@ body_linecount = Uatoi(big_buffer + 15); else if (Ustrncmp(p, "ody_zerocount", 13) == 0) body_zerocount = Uatoi(big_buffer + 15); - #ifdef EXPERIMENTAL_BRIGHTMAIL +#ifdef EXPERIMENTAL_BRIGHTMAIL else if (Ustrncmp(p, "mi_verdicts ", 12) == 0) bmi_verdicts = string_copy(big_buffer + 14); - #endif +#endif break; case 'd': if (Ustrcmp(p, "eliver_firsttime") == 0) deliver_firsttime = TRUE; - #ifdef EXPERIMENTAL_DSN /* Check if the dsn flags have been set in the header file */ else if (Ustrncmp(p, "sn_ret", 6) == 0) - { - dsn_ret= atoi(big_buffer + 8); - } + dsn_ret= atoi(CS big_buffer + 8); else if (Ustrncmp(p, "sn_envid", 8) == 0) - { dsn_envid = string_copy(big_buffer + 11); - } - #endif break; case 'f': if (Ustrncmp(p, "rozen", 5) == 0) { deliver_freeze = TRUE; - sscanf(big_buffer+7, TIME_T_FMT, &deliver_frozen_at); + sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at); } break; @@ -555,24 +574,32 @@ case 's': if (Ustrncmp(p, "ender_set_untrusted", 19) == 0) sender_set_untrusted = TRUE; - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN + else if (Ustrncmp(p, "pam_bar ", 8) == 0) + spam_bar = string_copy(big_buffer + 10); + else if (Ustrncmp(p, "pam_score ", 10) == 0) + spam_score = string_copy(big_buffer + 12); else if (Ustrncmp(p, "pam_score_int ", 14) == 0) spam_score_int = string_copy(big_buffer + 16); - #endif +#endif +#if defined(EXPERIMENTAL_INTERNATIONAL) && !defined(COMPILE_UTILITY) + else if (Ustrncmp(p, "mtputf8", 7) == 0) + message_smtputf8 = TRUE; +#endif break; - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS case 't': if (Ustrncmp(p, "ls_certificate_verified", 23) == 0) tls_in.certificate_verified = TRUE; else if (Ustrncmp(p, "ls_cipher", 9) == 0) tls_in.cipher = string_copy(big_buffer + 12); -#ifndef COMPILE_UTILITY +# ifndef COMPILE_UTILITY /* tls support fns not built in */ else if (Ustrncmp(p, "ls_ourcert", 10) == 0) (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert); else if (Ustrncmp(p, "ls_peercert", 11) == 0) (void) tls_import_cert(big_buffer + 14, &tls_in.peercert); -#endif +# endif else if (Ustrncmp(p, "ls_peerdn", 9) == 0) tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12)); else if (Ustrncmp(p, "ls_sni", 6) == 0) @@ -580,7 +607,16 @@ else if (Ustrncmp(p, "ls_ocsp", 7) == 0) tls_in.ocsp = big_buffer[10] - '0'; break; - #endif +#endif + +#if defined(EXPERIMENTAL_INTERNATIONAL) && !defined(COMPILE_UTILITY) + case 'u': + if (Ustrncmp(p, "tf8_downcvt", 11) == 0) + message_utf8_downconvert = 1; + else if (Ustrncmp(p, "tf8_optdowncvt", 15) == 0) + message_utf8_downconvert = -1; + break; +#endif default: /* Present because some compilers complain if all */ break; /* possibilities are not covered. */ @@ -631,10 +667,8 @@ { int nn; int pno = -1; - #ifdef EXPERIMENTAL_DSN int dsn_flags = 0; uschar *orcpt = NULL; - #endif uschar *errors_to = NULL; uschar *p; @@ -711,11 +745,9 @@ { int flags; - #ifdef EXPERIMENTAL_DSN - #ifndef COMPILE_UTILITY - DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 4 standard format spoolfile\n"); - #endif /* COMPILE_UTILITY */ - #endif +#if !defined (COMPILE_UTILITY) + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 4 standard format spoolfile\n"); +#endif (void)sscanf(CS p+1, "%d", &flags); @@ -733,7 +765,6 @@ } *(--p) = 0; /* Terminate address */ -#ifdef EXPERIMENTAL_DSN if ((flags & 0x02) != 0) /* one_time data exists */ { int len; @@ -748,14 +779,10 @@ } *(--p) = 0; /* Terminate address */ -#endif /* EXPERIMENTAL_DSN */ } -#ifdef EXPERIMENTAL_DSN - #ifndef COMPILE_UTILITY +#if !defined(COMPILE_UTILITY) else - { - DEBUG(D_deliver) debug_printf("**** SPOOL_IN - No additional fields\n"); - } + { DEBUG(D_deliver) debug_printf("**** SPOOL_IN - No additional fields\n"); } if ((orcpt != NULL) || (dsn_flags != 0)) { @@ -767,16 +794,13 @@ DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: |%s| errorsto: |%s|\n", big_buffer, errors_to); } - #endif /* COMPILE_UTILITY */ -#endif /* EXPERIMENTAL_DSN */ +#endif recipients_list[recipients_count].address = string_copy(big_buffer); recipients_list[recipients_count].pno = pno; recipients_list[recipients_count].errors_to = errors_to; - #ifdef EXPERIMENTAL_DSN recipients_list[recipients_count].orcpt = orcpt; recipients_list[recipients_count].dsn_flags = dsn_flags; - #endif } /* The remainder of the spool header file contains the headers for the message, @@ -859,9 +883,9 @@ { n = errno; - #ifndef COMPILE_UTILITY +#ifndef COMPILE_UTILITY DEBUG(D_any) debug_printf("Error while reading spool file %s\n", name); - #endif /* COMPILE_UTILITY */ +#endif /* COMPILE_UTILITY */ fclose(f); errno = n; diff -Nru exim4-4.84/src/spool_mbox.c exim4-4.86~RC4/src/spool_mbox.c --- exim4-4.84/src/spool_mbox.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/spool_mbox.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Tom Kistner 2003-???? */ +/* Copyright (c) Tom Kistner 2003 - 2015 */ /* License: GPL */ /* Code for setting up a MBOX style spool file inside a /scan/ @@ -26,7 +26,9 @@ /* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size * normally, source_file_override is NULL */ -FILE *spool_mbox(unsigned long *mbox_file_size, uschar *source_file_override) { +FILE * +spool_mbox(unsigned long *mbox_file_size, const uschar *source_file_override) +{ uschar message_subdir[2]; uschar buffer[16384]; uschar *temp_string; diff -Nru exim4-4.84/src/spool_out.c exim4-4.86~RC4/src/spool_out.c --- exim4-4.84/src/spool_out.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/spool_out.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for writing spool files, and moving them about. */ @@ -218,7 +218,9 @@ if (local_error_message) fprintf(f, "-localerror\n"); if (local_scan_data != NULL) fprintf(f, "-local_scan %s\n", local_scan_data); #ifdef WITH_CONTENT_SCAN -if (spam_score_int != NULL) fprintf(f,"-spam_score_int %s\n", spam_score_int); +if (spam_bar) fprintf(f,"-spam_bar %s\n", spam_bar); +if (spam_score) fprintf(f,"-spam_score %s\n", spam_score); +if (spam_score_int) fprintf(f,"-spam_score_int %s\n", spam_score_int); #endif if (deliver_manual_thaw) fprintf(f, "-manual_thaw\n"); if (sender_set_untrusted) fprintf(f, "-sender_set_untrusted\n"); @@ -245,13 +247,20 @@ if (tls_in.ocsp) fprintf(f, "-tls_ocsp %d\n", tls_in.ocsp); #endif -#ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_INTERNATIONAL +if (message_smtputf8) + { + fprintf(f, "-smtputf8\n"); + if (message_utf8_downconvert) + fprintf(f, "-utf8_%sdowncvt\n", message_utf8_downconvert < 0 ? "opt" : ""); + } +#endif + /* Write the dsn flags to the spool header file */ DEBUG(D_deliver) debug_printf("DSN: Write SPOOL :-dsn_envid %s\n", dsn_envid); if (dsn_envid != NULL) fprintf(f, "-dsn_envid %s\n", dsn_envid); DEBUG(D_deliver) debug_printf("DSN: Write SPOOL :-dsn_ret %d\n", dsn_ret); if (dsn_ret != 0) fprintf(f, "-dsn_ret %d\n", dsn_ret); -#endif /* To complete the envelope, write out the tree of non-recipients, followed by the list of recipients. These won't be disjoint the first time, when no @@ -263,34 +272,21 @@ for (i = 0; i < recipients_count; i++) { recipient_item *r = recipients_list + i; -#ifdef EXPERIMENTAL_DSN DEBUG(D_deliver) debug_printf("DSN: Flags :%d\n", r->dsn_flags); -#endif - if (r->pno < 0 && r->errors_to == NULL - #ifdef EXPERIMENTAL_DSN - && r->dsn_flags == 0 - #endif - ) + if (r->pno < 0 && r->errors_to == NULL && r->dsn_flags == 0) fprintf(f, "%s\n", r->address); else { uschar *errors_to = (r->errors_to == NULL)? US"" : r->errors_to; - #ifdef EXPERIMENTAL_DSN /* for DSN SUPPORT extend exim 4 spool in a compatible way by adding new values upfront and add flag 0x02 */ uschar *orcpt = (r->orcpt == NULL)? US"" : r->orcpt; fprintf(f, "%s %s %d,%d %s %d,%d#3\n", r->address, orcpt, Ustrlen(orcpt), r->dsn_flags, errors_to, Ustrlen(errors_to), r->pno); - #else - fprintf(f, "%s %s %d,%d#1\n", r->address, errors_to, - Ustrlen(errors_to), r->pno); - #endif } - #ifdef EXPERIMENTAL_DSN DEBUG(D_deliver) debug_printf("DSN: **** SPOOL_OUT - address: |%s| errorsto: |%s| orcpt: |%s| dsn_flags: %d\n", r->address, r->errors_to, r->orcpt, r->dsn_flags); - #endif } /* Put a blank line before the headers */ @@ -519,3 +515,5 @@ #endif /* End of spool_out.c */ +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/string.c exim4-4.86~RC4/src/string.c --- exim4-4.84/src/string.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/string.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Miscellaneous string-handling functions. Some are not required for @@ -165,7 +165,7 @@ uschar * string_format_size(int size, uschar *buffer) { -if (size == 0) Ustrcpy(CS buffer, " "); +if (size == 0) Ustrcpy(buffer, " "); else if (size < 1024) sprintf(CS buffer, "%5d", size); else if (size < 10*1024) sprintf(CS buffer, "%4.1fK", (double)size / 1024.0); @@ -224,13 +224,13 @@ */ int -string_interpret_escape(uschar **pp) +string_interpret_escape(const uschar **pp) { #ifdef COMPILE_UTILITY const uschar *hex_digits= CUS"0123456789abcdef"; #endif int ch; -uschar *p = *pp; +const uschar *p = *pp; ch = *(++p); if (isdigit(ch) && ch != '8' && ch != '9') { @@ -284,12 +284,12 @@ Returns: string with non-printers encoded as printing sequences */ -uschar * -string_printing2(uschar *s, BOOL allow_tab) +const uschar * +string_printing2(const uschar *s, BOOL allow_tab) { int nonprintcount = 0; int length = 0; -uschar *t = s; +const uschar *t = s; uschar *ss, *tt; while (*t != 0) @@ -374,7 +374,7 @@ { if (*p == '\\') { - *q++ = string_interpret_escape(&p); + *q++ = string_interpret_escape((const uschar **)&p); p++; } else @@ -437,7 +437,7 @@ */ uschar * -string_copy_malloc(uschar *s) +string_copy_malloc(const uschar *s) { int len = Ustrlen(s) + 1; uschar *ss = store_malloc(len); @@ -457,7 +457,7 @@ */ uschar * -string_copylc(uschar *s) +string_copylc(const uschar *s) { uschar *ss = store_get(Ustrlen(s) + 1); uschar *p = ss; @@ -483,7 +483,7 @@ */ uschar * -string_copyn(uschar *s, int n) +string_copyn(const uschar *s, int n) { uschar *ss = store_get(n + 1); Ustrncpy(ss, s, n); @@ -639,9 +639,9 @@ */ uschar * -string_dequote(uschar **sptr) +string_dequote(const uschar **sptr) { -uschar *s = *sptr; +const uschar *s = *sptr; uschar *t, *yield; /* First find the end of the string */ @@ -868,10 +868,10 @@ */ uschar * -string_nextinlist(uschar **listptr, int *separator, uschar *buffer, int buflen) +string_nextinlist(const uschar **listptr, int *separator, uschar *buffer, int buflen) { -register int sep = *separator; -register uschar *s = *listptr; +int sep = *separator; +const uschar *s = *listptr; BOOL sep_is_special; if (s == NULL) return NULL; @@ -928,7 +928,7 @@ { int size = 0; int ptr = 0; - uschar *ss; + const uschar *ss; /* We know that *s != 0 at this point. However, it might be pointing to a separator, which could indicate an empty string, or (if an ispunct() @@ -1008,6 +1008,51 @@ new[off] = '\0'; return new; } + + +static const uschar * +Ustrnchr(const uschar * s, int c, unsigned * len) +{ +unsigned siz = *len; +while (siz) + { + if (!*s) return NULL; + if (*s == c) + { + *len = siz; + return s; + } + s++; + siz--; + } +return NULL; +} + +uschar * +string_append_listele_n(uschar * list, uschar sep, const uschar * ele, + unsigned len) +{ +uschar * new = NULL; +int sz = 0, off = 0; +const uschar * sp; + +if (list) + { + new = string_cat(new, &sz, &off, list, Ustrlen(list)); + new = string_cat(new, &sz, &off, &sep, 1); + } + +while((sp = Ustrnchr(ele, sep, &len))) + { + new = string_cat(new, &sz, &off, ele, sp-ele+1); + new = string_cat(new, &sz, &off, &sep, 1); + ele = sp+1; + len--; + } +new = string_cat(new, &sz, &off, ele, len); +new[off] = '\0'; +return new; +} #endif /* COMPILE_UTILITY */ @@ -1502,14 +1547,35 @@ string_get_localpart(address_item *addr, uschar *yield, int *sizeptr, int *ptrptr) { -if (testflag(addr, af_include_affixes) && addr->prefix != NULL) - yield = string_cat(yield, sizeptr, ptrptr, addr->prefix, - Ustrlen(addr->prefix)); -yield = string_cat(yield, sizeptr, ptrptr, addr->local_part, - Ustrlen(addr->local_part)); -if (testflag(addr, af_include_affixes) && addr->suffix != NULL) - yield = string_cat(yield, sizeptr, ptrptr, addr->suffix, - Ustrlen(addr->suffix)); +uschar * s; + +s = addr->prefix; +if (testflag(addr, af_include_affixes) && s) + { +#ifdef EXPERIMENTAL_INTERNATIONAL + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, sizeptr, ptrptr, s, Ustrlen(s)); + } + +s = addr->local_part; +#ifdef EXPERIMENTAL_INTERNATIONAL +if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif +yield = string_cat(yield, sizeptr, ptrptr, s, Ustrlen(s)); + +s = addr->suffix; +if (testflag(addr, af_include_affixes) && s) + { +#ifdef EXPERIMENTAL_INTERNATIONAL + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, sizeptr, ptrptr, s, Ustrlen(s)); + } + return yield; } @@ -1570,10 +1636,15 @@ { if (addr->local_part != NULL) { + const uschar * s; yield = string_get_localpart(addr, yield, &size, &ptr); yield = string_cat(yield, &size, &ptr, US"@", 1); - yield = string_cat(yield, &size, &ptr, addr->domain, - Ustrlen(addr->domain) ); + s = addr->domain; +#ifdef EXPERIMENTAL_INTERNATIONAL + if (testflag(addr, af_utf8_downcvt)) + s = string_localpart_utf8_to_alabel(s, NULL); +#endif + yield = string_cat(yield, &size, &ptr, s, Ustrlen(s) ); } else { diff -Nru exim4-4.84/src/structs.h exim4-4.86~RC4/src/structs.h --- exim4-4.84/src/structs.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/structs.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -59,8 +59,8 @@ typedef struct host_item { struct host_item *next; - uschar *name; /* Host name */ - uschar *address; /* IP address in text form */ + const uschar *name; /* Host name */ + const uschar *address; /* IP address in text form */ int port; /* port value in host order (if SRV lookup) */ int mx; /* MX value if found via MX records */ int sort_key; /* MX*1000 plus random "fraction" */ @@ -148,6 +148,7 @@ uschar *home_dir; /* ) Used only for local transports */ uschar *current_dir; /* ) */ /**************************************/ + uschar *expand_multi_domain; /* ) */ BOOL multi_domain; /* ) */ BOOL overrides_hosts; /* ) Used only for remote transports */ int max_addresses; /* ) */ @@ -187,8 +188,8 @@ BOOL log_fail_output; BOOL log_defer_output; BOOL retry_use_local_part; /* Defaults true for local, false for remote */ -#ifdef EXPERIMENTAL_TPDA - uschar *tpda_delivery_action; /* String to expand on success */ +#ifdef EXPERIMENTAL_EVENT + uschar *event_action; /* String to expand on notable events */ #endif } transport_instance; @@ -217,6 +218,11 @@ +typedef struct { + uschar *request; + uschar *require; +} dnssec_domains; + /* Structure for holding information about the configured routers. */ typedef struct router_instance { @@ -285,9 +291,7 @@ BOOL verify_sender; /* Use this router when verifying a sender */ BOOL uid_set; /* Flag to indicate uid is set */ BOOL unseen; /* If TRUE carry on, even after success */ -#ifdef EXPERIMENTAL_DSN BOOL dsn_lasthop; /* If TRUE, this router is a DSN endpoint */ -#endif int self_code; /* Encoded version of "self" */ uid_t uid; /* Fixed uid value */ @@ -297,6 +301,8 @@ transport_instance *transport; /* Transport block (when found) */ struct router_instance *pass_router; /* Actual router for passed address */ struct router_instance *redirect_router; /* Actual router for generated address */ + + dnssec_domains dnssec; } router_instance; @@ -460,6 +466,11 @@ #ifdef EXPERIMENTAL_SRS uschar *srs_sender; /* Change return path when delivering */ #endif + #ifdef EXPERIMENTAL_INTERNATIONAL + BOOL utf8_msg:1; /* requires SMTPUTF8 processing */ + BOOL utf8_downcvt:1; /* mandatory downconvert on delivery */ + BOOL utf8_downcvt_maybe:1; /* optional downconvert on delivery */ + #endif } address_item_propagated; /* Bits for the flags field below */ @@ -495,6 +506,12 @@ # define af_prdr_used 0x08000000 /* delivery used SMTP PRDR */ #endif #define af_force_command 0x10000000 /* force_command in pipe transport */ +#ifdef EXPERIMENTAL_DANE +# define af_dane_verified 0x20000000 /* TLS cert verify done with DANE */ +#endif +#ifdef EXPERIMENTAL_INTERNATIONAL +# define af_utf8_downcvt 0x40000000 /* downconvert was done for delivery */ +#endif /* These flags must be propagated when a child is created */ @@ -528,7 +545,7 @@ uschar *local_part; /* points to cc or lc version */ uschar *prefix; /* stripped prefix of local part */ uschar *suffix; /* stripped suffix of local part */ - uschar *domain; /* working domain (lower cased) */ + const uschar *domain; /* working domain (lower cased) */ uschar *address_retry_key; /* retry key including full address */ uschar *domain_retry_key; /* retry key for domain only */ @@ -556,11 +573,9 @@ uschar *auth_id; /* auth "login" name used by transport */ uschar *auth_sndr; /* AUTH arg to SMTP MAIL, used by transport */ - #ifdef EXPERIMENTAL_DSN uschar *dsn_orcpt; /* DSN orcpt value */ int dsn_flags; /* DSN flags */ int dsn_aware; /* DSN aware flag */ - #endif uid_t uid; /* uid for transporting */ gid_t gid; /* gid for transporting */ @@ -581,7 +596,7 @@ /* ( also */ /* ( contains verify rc in sender verify cache */ short int transport_return; /* result of delivery attempt */ - address_item_propagated p; /* fields that are propagated to children */ + address_item_propagated prop; /* fields that are propagated to children */ } address_item; /* The table of header names consists of items of this type */ @@ -597,7 +612,7 @@ typedef struct error_block { struct error_block *next; - uschar *text1; + const uschar *text1; uschar *text2; } error_block; @@ -750,9 +765,9 @@ /* Structure for passing arguments to check_host() */ typedef struct check_host_block { - uschar *host_name; - uschar *host_address; - uschar *host_ipv4; + const uschar *host_name; + const uschar *host_address; + const uschar *host_ipv4; BOOL negative; } check_host_block; @@ -768,7 +783,7 @@ /* Structure for holding data for an entry in a named list */ typedef struct namedlist_block { - uschar *string; /* the list string */ + const uschar *string; /* the list string */ namedlist_cacheblock *cache_data; /* cached domain_data or localpart_data */ int number; /* the number of the list for caching */ } namedlist_block; @@ -791,4 +806,7 @@ int verb; } acl_block; +/* smtp transport calc outbound_ip */ +typedef BOOL (*oicf) (uschar *message_id, void *data); + /* End of structs.h */ diff -Nru exim4-4.84/src/tls.c exim4-4.86~RC4/src/tls.c --- exim4-4.84/src/tls.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/tls.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2012 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is @@ -81,6 +81,28 @@ /************************************************* +* Timezone environment flipping * +*************************************************/ + +static uschar * +to_tz(uschar * tz) +{ + uschar * old = US getenv("TZ"); + setenv("TZ", CS tz, 1); + tzset(); + return old; +} +static void +restore_tz(uschar * tz) +{ + if (tz) + setenv("TZ", CS tz, 1); + else + unsetenv("TZ"); + tzset(); +} + +/************************************************* * Many functions are package-specific * *************************************************/ @@ -224,7 +246,7 @@ Arguments: dn Distinguished Name string - mod string containing optional list-sep and + mod list containing optional output list-sep and field selector match, comma-separated Return: allocated string with list of matching fields, @@ -232,7 +254,7 @@ */ uschar * -tls_field_from_dn(uschar * dn, uschar * mod) +tls_field_from_dn(uschar * dn, const uschar * mod) { int insep = ','; uschar outsep = '\n'; @@ -245,19 +267,20 @@ if (ele[0] != '>') match = ele; /* field tag to match */ else if (ele[1]) - outsep = ele[1]; /* nondefault separator */ + outsep = ele[1]; /* nondefault output separator */ dn_to_list(dn); insep = ','; -len = Ustrlen(match); -while ((ele = string_nextinlist(&dn, &insep, NULL, 0))) - if (Ustrncmp(ele, match, len) == 0 && ele[len] == '=') +len = match ? Ustrlen(match) : -1; +while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0))) + if ( !match + || Ustrncmp(ele, match, len) == 0 && ele[len] == '=' + ) list = string_append_listele(list, outsep, ele+len+1); return list; } -# ifdef EXPERIMENTAL_CERTNAMES /* Compare a domain name with a possibly-wildcarded name. Wildcards are restricted to a single one, as the first element of patterns having at least three dot-separated elements. Case-independent. @@ -290,7 +313,7 @@ */ BOOL -tls_is_name_for_cert(uschar * namelist, void * cert) +tls_is_name_for_cert(const uschar * namelist, void * cert) { uschar * altnames = tls_cert_subject_altname(cert, US"dns"); uschar * subjdn; @@ -303,7 +326,7 @@ int alt_sep = '\n'; while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { - uschar * an = altnames; + const uschar * an = altnames; while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0))) if (is_name_match(cmpname, certname)) return TRUE; @@ -317,7 +340,7 @@ dn_to_list(subjdn); while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { - uschar * sn = subjdn; + const uschar * sn = subjdn; while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0))) if ( *certname++ == 'C' && *certname++ == 'N' @@ -329,7 +352,6 @@ } return FALSE; } -# endif /*EXPERIMENTAL_CERTNAMES*/ #endif /*SUPPORT_TLS*/ /* vi: aw ai sw=2 diff -Nru exim4-4.84/src/tlscert-gnu.c exim4-4.86~RC4/src/tlscert-gnu.c --- exim4-4.84/src/tlscert-gnu.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/tlscert-gnu.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Jeremy Harris 2014 */ +/* Copyright (c) Jeremy Harris 2014 - 2015 */ /* This file provides TLS/SSL support for Exim using the GnuTLS library, one of the available supported implementations. This file is #included into @@ -27,7 +27,7 @@ size_t sz = buflen; void * reset_point = store_get(0); int fail; -uschar * cp; +const uschar * cp; if ((fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert, GNUTLS_X509_FMT_PEM, buf, &sz))) @@ -51,10 +51,14 @@ { void * reset_point = store_get(0); gnutls_datum_t datum; -gnutls_x509_crt_t crt; +gnutls_x509_crt_t crt = *(gnutls_x509_crt_t *)cert; int fail = 0; -gnutls_global_init(); +if (crt) + gnutls_x509_crt_deinit(crt); +else + gnutls_global_init(); + gnutls_x509_crt_init(&crt); datum.data = string_unprinting(US buf); @@ -73,10 +77,15 @@ } void -tls_free_cert(void * cert) +tls_free_cert(void ** cert) { -gnutls_x509_crt_deinit((gnutls_x509_crt_t) cert); -gnutls_global_deinit(); +gnutls_x509_crt_t crt = *(gnutls_x509_crt_t *)cert; +if (crt) + { + gnutls_x509_crt_deinit(crt); + gnutls_global_deinit(); + *cert = NULL; + } } /***************************************************** @@ -98,15 +107,20 @@ time_copy(time_t t, uschar * mod) { uschar * cp; -struct tm * tp; -size_t len; +size_t len = 32; if (mod && Ustrcmp(mod, "int") == 0) return string_sprintf("%u", (unsigned)t); -cp = store_get(32); -tp = gmtime(&t); -len = strftime(CS cp, 32, "%b %e %T %Y %Z", tp); +cp = store_get(len); +if (timestamps_utc) + { + uschar * tz = to_tz(US"GMT0"); + len = strftime(CS cp, len, "%b %e %T %Y %Z", gmtime(&t)); + restore_tz(tz); + } +else + len = strftime(CS cp, len, "%b %e %T %Y %Z", localtime(&t)); return len > 0 ? cp : NULL; } @@ -177,7 +191,7 @@ uschar * tls_cert_signature(void * cert, uschar * mod) { -uschar * cp1; +uschar * cp1 = NULL; uschar * cp2; uschar * cp3; size_t len = 0; diff -Nru exim4-4.84/src/tlscert-openssl.c exim4-4.86~RC4/src/tlscert-openssl.c --- exim4-4.84/src/tlscert-openssl.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/tlscert-openssl.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) Jeremy Harris 2014 */ +/* Copyright (c) Jeremy Harris 2014 - 2015 */ /* This module provides TLS (aka SSL) support for Exim using the OpenSSL library. It is #included into the tls.c file when that library is used. @@ -55,9 +55,11 @@ void * reset_point = store_get(0); const uschar * cp = string_unprinting(US buf); BIO * bp; -X509 * x; +X509 * x = *(X509 **)cert; int fail = 0; +if (x) X509_free(x); + bp = BIO_new_mem_buf(US cp, -1); if (!(x = PEM_read_bio_X509(bp, NULL, 0, NULL))) { @@ -73,9 +75,14 @@ } void -tls_free_cert(void * cert) +tls_free_cert(void ** cert) { -X509_free((X509 *)cert); +X509 * x = *(X509 **)cert; +if (x) + { + X509_free(x); + *cert = NULL; + } } @@ -103,30 +110,65 @@ } static uschar * -bio_string_time_to_int(BIO * bp, int len) -{ -uschar * cp = US""; -struct tm t; -len = len > 0 ? (int) BIO_get_mem_data(bp, &cp) : 0; -/*XXX %Z might be glibc-specific? */ -(void) strptime(CS cp, "%b%t%e%t%T%t%Y%t%Z", &t); -BIO_free(bp); -/*XXX timegm might not be portable? */ -return string_sprintf("%u", (unsigned) timegm(&t)); -} - -static uschar * -asn1_time_copy(const ASN1_TIME * time, uschar * mod) +asn1_time_copy(const ASN1_TIME * asntime, uschar * mod) { +uschar * s = NULL; BIO * bp = BIO_new(BIO_s_mem()); int len; -if (!bp) return badalloc(); +if (!bp) + return badalloc(); +len = ASN1_TIME_print(bp, asntime); +len = len > 0 ? (int) BIO_get_mem_data(bp, &s) : 0; -len = ASN1_TIME_print(bp, time); -return mod && Ustrcmp(mod, "int") == 0 - ? bio_string_time_to_int(bp, len) - : bio_string_copy(bp, len); +if (mod && Ustrcmp(mod, "raw") == 0) /* native ASN */ + s = string_copyn(s, len); +else + { + struct tm tm; + struct tm * tm_p = &tm; + BOOL mod_tz; + uschar * tz = to_tz(US"GMT0"); /* need to call strptime with baseline TZ */ + + /* Parse OpenSSL ASN1_TIME_print output. A shame there seems to + be no other interface for the times. + */ + + /*XXX %Z might be glibc-specific? Solaris has it, at least*/ + /*XXX should we switch to POSIX locale for this? */ + tm.tm_isdst = 0; + if (!strptime(CCS s, "%b %e %T %Y %Z", &tm)) + expand_string_message = US"failed time conversion"; + + else + { + time_t t = mktime(&tm); /* make the tm self-consistent */ + + if (mod && Ustrcmp(mod, "int") == 0) /* seconds since epoch */ + s = string_sprintf("%u", t); + + else + { + if (!timestamps_utc) /* decoded string in local TZ */ + { /* shift to local TZ */ + restore_tz(tz); + mod_tz = FALSE; + tm_p = localtime(&t); + } + /* "utc" is default, and rfc5280 says cert times should be Zulu */ + + /* convert to string in our format */ + len = 32; + s = store_get(len); + strftime(CS s, (size_t)len, "%b %e %T %Y %z", tm_p); + } + } + + if (mod_tz); + restore_tz(tz); + } +BIO_free(bp); +return s; } static uschar * @@ -296,7 +338,7 @@ uschar * list = NULL; STACK_OF(GENERAL_NAME) * san = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i((X509 *)cert, NID_subject_alt_name, NULL, NULL); -uschar sep = '\n'; +uschar osep = '\n'; uschar * tag = US""; uschar * ele; int match = -1; @@ -304,16 +346,15 @@ if (!san) return NULL; -while (mod) +while (mod && *mod) { - if (*mod == '>' && *++mod) sep = *mod++; - else if (Ustrcmp(mod, "dns")==0) { match = GEN_DNS; mod += 3; } - else if (Ustrcmp(mod, "uri")==0) { match = GEN_URI; mod += 3; } - else if (Ustrcmp(mod, "mail")==0) { match = GEN_EMAIL; mod += 4; } - else continue; + if (*mod == '>' && *++mod) osep = *mod++; + else if (Ustrncmp(mod,"dns",3)==0) { match = GEN_DNS; mod += 3; } + else if (Ustrncmp(mod,"uri",3)==0) { match = GEN_URI; mod += 3; } + else if (Ustrncmp(mod,"mail",4)==0) { match = GEN_EMAIL; mod += 4; } + else mod++; - if (*mod++ != ',') - break; + if (*mod == ',') mod++; } while (sk_GENERAL_NAME_num(san) > 0) @@ -344,8 +385,8 @@ if (ele[len]) /* not nul-terminated */ ele = string_copyn(ele, len); - if (strnlen(CS ele, len) == len) /* ignore any with embedded nul */ - list = string_append_listele(list, sep, + if (Ustrlen(ele) == len) /* ignore any with embedded nul */ + list = string_append_listele(list, osep, match == -1 ? string_sprintf("%s=%s", tag, ele) : ele); } @@ -371,9 +412,13 @@ ACCESS_DESCRIPTION * ad = sk_ACCESS_DESCRIPTION_value(ads, i); if (ad && OBJ_obj2nid(ad->method) == NID_ad_OCSP) - list = string_append_listele(list, sep, - ASN1_STRING_data(ad->location->d.ia5)); + { + uschar * ele = ASN1_STRING_data(ad->location->d.ia5); + int len = ASN1_STRING_length(ad->location->d.ia5); + list = string_append_listele_n(list, sep, ele, len); + } } +sk_ACCESS_DESCRIPTION_free(ads); return list; } @@ -404,9 +449,13 @@ if ( (np = sk_GENERAL_NAME_value(names, j)) && np->type == GEN_URI ) - list = string_append_listele(list, sep, - ASN1_STRING_data(np->d.uniformResourceIdentifier)); + { + uschar * ele = ASN1_STRING_data(np->d.uniformResourceIdentifier); + int len = ASN1_STRING_length(np->d.uniformResourceIdentifier); + list = string_append_listele_n(list, sep, ele, len); + } } +sk_DIST_POINT_free(dps); return list; } diff -Nru exim4-4.84/src/tls-gnu.c exim4-4.86~RC4/src/tls-gnu.c --- exim4-4.84/src/tls-gnu.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/tls-gnu.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Copyright (c) Phil Pennock 2012 */ @@ -47,6 +47,18 @@ # warning "GnuTLS library version too old; define DISABLE_OCSP in Makefile" # define DISABLE_OCSP #endif +#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_EVENT) +# warning "GnuTLS library version too old; tls:cert event unsupported" +# undef EXPERIMENTAL_EVENT +#endif +#if GNUTLS_VERSION_NUMBER >= 0x030306 +# define SUPPORT_CA_DIR +#else +# undef SUPPORT_CA_DIR +#endif +#if GNUTLS_VERSION_NUMBER >= 0x030314 +# define SUPPORT_SYSDEFAULT_CABUNDLE +#endif #ifndef DISABLE_OCSP # include @@ -66,11 +78,7 @@ /* Values for verify_requirement */ enum peer_verify_requirement - { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED -#ifdef EXPERIMENTAL_CERTNAMES - ,VERIFY_WITHHOST -#endif - }; + { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED }; /* This holds most state for server or client; with this, we can set up an outbound TLS-enabled connection in an ACL callout, while not stomping all @@ -112,8 +120,9 @@ uschar *exp_tls_crl; uschar *exp_tls_require_ciphers; uschar *exp_tls_ocsp_file; -#ifdef EXPERIMENTAL_CERTNAMES - uschar *exp_tls_verify_cert_hostnames; + const uschar *exp_tls_verify_cert_hostnames; +#ifdef EXPERIMENTAL_EVENT + uschar *event_action; #endif tls_support *tlsp; /* set in tls_init() */ @@ -130,7 +139,8 @@ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -#ifdef EXPERIMENTAL_CERTNAMES + NULL, +#ifdef EXPERIMENTAL_EVENT NULL, #endif NULL, @@ -144,7 +154,9 @@ single-threaded to keep from processing data on an inbound TLS connection while talking to another TLS connection for an outbound check. This does mean that there's no way for heart-beats to be responded to, for the duration of the -second connection. */ +second connection. +XXX But see gnutls_session_get_ptr() +*/ static exim_gnutls_state_st state_server, state_client; @@ -174,18 +186,18 @@ the library logging; a value less than 0 disables the calls to set up logging callbacks. */ #ifndef EXIM_GNUTLS_LIBRARY_LOG_LEVEL -#define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1 +# define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1 #endif #ifndef EXIM_CLIENT_DH_MIN_BITS -#define EXIM_CLIENT_DH_MIN_BITS 1024 +# define EXIM_CLIENT_DH_MIN_BITS 1024 #endif /* With GnuTLS 2.12.x+ we have gnutls_sec_param_to_pk_bits() with which we can ask for a bit-strength. Without that, we stick to the constant we had before, for now. */ #ifndef EXIM_SERVER_DH_BITS_PRE2_12 -#define EXIM_SERVER_DH_BITS_PRE2_12 1024 +# define EXIM_SERVER_DH_BITS_PRE2_12 1024 #endif #define exim_gnutls_err_check(Label) do { \ @@ -256,7 +268,7 @@ { if (host) { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s%s", + log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s)%s%s", host->name, host->address, prefix, msg ? ": " : "", msg ? msg : ""); return FAIL; } @@ -265,6 +277,7 @@ uschar *conn_info = smtp_get_connection_info(); if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; + /* I'd like to get separated H= here, but too hard for now */ log_write(0, LOG_MAIN, "TLS error on %s (%s)%s%s", conn_info, prefix, msg ? ": " : "", msg ? msg : ""); return DEFER; @@ -844,6 +857,10 @@ { if (!expand_check_tlsvar(tls_verify_certificates)) return DEFER; +#ifndef SUPPORT_SYSDEFAULT_CABUNDLE + if (Ustrcmp(state->exp_tls_verify_certificates, "system") == 0) + state->exp_tls_verify_certificates = NULL; +#endif if (state->tls_crl && *state->tls_crl) if (!expand_check_tlsvar(tls_crl)) return DEFER; @@ -864,46 +881,65 @@ return OK; } -if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0) +#ifdef SUPPORT_SYSDEFAULT_CABUNDLE +if (Ustrcmp(state->exp_tls_verify_certificates, "system") == 0) + cert_count = gnutls_certificate_set_x509_system_trust(state->x509_cred); +else +#endif { - log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s " - "(tls_verify_certificates): %s", state->exp_tls_verify_certificates, - strerror(errno)); - return DEFER; - } + if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s " + "(tls_verify_certificates): %s", state->exp_tls_verify_certificates, + strerror(errno)); + return DEFER; + } -/* The test suite passes in /dev/null; we could check for that path explicitly, -but who knows if someone has some weird FIFO which always dumps some certs, or -other weirdness. The thing we really want to check is that it's not a -directory, since while OpenSSL supports that, GnuTLS does not. -So s/!S_ISREG/S_ISDIR/ and change some messsaging ... */ -if (S_ISDIR(statbuf.st_mode)) - { - DEBUG(D_tls) - debug_printf("verify certificates path is a dir: \"%s\"\n", - state->exp_tls_verify_certificates); - log_write(0, LOG_MAIN|LOG_PANIC, - "tls_verify_certificates \"%s\" is a directory", - state->exp_tls_verify_certificates); - return DEFER; - } +#ifndef SUPPORT_CA_DIR + /* The test suite passes in /dev/null; we could check for that path explicitly, + but who knows if someone has some weird FIFO which always dumps some certs, or + other weirdness. The thing we really want to check is that it's not a + directory, since while OpenSSL supports that, GnuTLS does not. + So s/!S_ISREG/S_ISDIR/ and change some messsaging ... */ + if (S_ISDIR(statbuf.st_mode)) + { + DEBUG(D_tls) + debug_printf("verify certificates path is a dir: \"%s\"\n", + state->exp_tls_verify_certificates); + log_write(0, LOG_MAIN|LOG_PANIC, + "tls_verify_certificates \"%s\" is a directory", + state->exp_tls_verify_certificates); + return DEFER; + } +#endif -DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n", - state->exp_tls_verify_certificates, statbuf.st_size); + DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n", + state->exp_tls_verify_certificates, statbuf.st_size); -if (statbuf.st_size == 0) - { - DEBUG(D_tls) - debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n"); - return OK; + if (statbuf.st_size == 0) + { + DEBUG(D_tls) + debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n"); + return OK; + } + + cert_count = + +#ifdef SUPPORT_CA_DIR + (statbuf.st_mode & S_IFMT) == S_IFDIR + ? + gnutls_certificate_set_x509_trust_dir(state->x509_cred, + CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM) + : +#endif + gnutls_certificate_set_x509_trust_file(state->x509_cred, + CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM); } -cert_count = gnutls_certificate_set_x509_trust_file(state->x509_cred, - CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM); if (cert_count < 0) { rc = cert_count; - exim_gnutls_err_check(US"gnutls_certificate_set_x509_trust_file"); + exim_gnutls_err_check(US"setting certificate trust"); } DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count); @@ -1359,11 +1395,10 @@ else { -#ifdef EXPERIMENTAL_CERTNAMES - if (state->verify_requirement == VERIFY_WITHHOST) + if (state->exp_tls_verify_cert_hostnames) { int sep = 0; - uschar * list = state->exp_tls_verify_cert_hostnames; + const uschar * list = state->exp_tls_verify_cert_hostnames; uschar * name; while (name = string_nextinlist(&list, &sep, NULL, 0)) if (gnutls_x509_crt_check_hostname(state->tlsp->peercert, CS name)) @@ -1372,12 +1407,15 @@ { DEBUG(D_tls) debug_printf("TLS certificate verification failed: cert name mismatch\n"); - gnutls_alert_send(state->session, - GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); - return FALSE; + if (state->verify_requirement >= VERIFY_REQUIRED) + { + gnutls_alert_send(state->session, + GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); + return FALSE; + } + return TRUE; } } -#endif state->peer_cert_verified = TRUE; DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=\"%s\"\n", state->peerdn ? state->peerdn : US""); @@ -1512,6 +1550,54 @@ #endif +#ifdef EXPERIMENTAL_EVENT +/* +We use this callback to get observability and detail-level control +for an exim TLS connection (either direction), raising a tls:cert event +for each cert in the chain presented by the peer. Any event +can deny verification. + +Return 0 for the handshake to continue or non-zero to terminate. +*/ + +static int +verify_cb(gnutls_session_t session) +{ +const gnutls_datum * cert_list; +unsigned int cert_list_size = 0; +gnutls_x509_crt_t crt; +int rc; +uschar * yield; +exim_gnutls_state_st * state = gnutls_session_get_ptr(session); + +cert_list = gnutls_certificate_get_peers(session, &cert_list_size); +if (cert_list) + while (cert_list_size--) + { + rc = import_cert(&cert_list[cert_list_size], &crt); + if (rc != GNUTLS_E_SUCCESS) + { + DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n", + cert_list_size, gnutls_strerror(rc)); + break; + } + + state->tlsp->peercert = crt; + if ((yield = event_raise(state->event_action, + US"tls:cert", string_sprintf("%d", cert_list_size)))) + { + log_write(0, LOG_MAIN, + "SSL verify denied by event-action: depth=%d: %s", + cert_list_size, yield); + return 1; /* reject */ + } + state->tlsp->peercert = NULL; + } + +return 0; +} + +#endif @@ -1588,6 +1674,15 @@ gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE); } +#ifdef EXPERIMENTAL_EVENT +if (event_action) + { + state->event_action = event_action; + gnutls_session_set_ptr(state->session, state); + gnutls_certificate_set_verify_function(state->x509_cred, verify_cb); + } +#endif + /* Register SNI handling; always, even if not in tls_certificate, so that the expansion variable $tls_sni is always available. */ @@ -1684,6 +1779,25 @@ +static void +tls_client_setup_hostname_checks(host_item * host, exim_gnutls_state_st * state, + smtp_transport_options_block * ob) +{ +if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) + { + state->exp_tls_verify_cert_hostnames = +#ifdef EXPERIMENTAL_INTERNATIONAL + string_domain_utf8_to_alabel(host->name, NULL); +#else + host->name; +#endif + DEBUG(D_tls) + debug_printf("TLS: server cert verification includes hostname: \"%s\".\n", + state->exp_tls_verify_cert_hostnames); + } +} + + /************************************************* * Start a TLS session in a client * *************************************************/ @@ -1694,7 +1808,7 @@ fd the fd of the connection host connected host (for messages) addr the first address (not used) - ob smtp transport options + tb transport (always smtp) Returns: OK/DEFER/FAIL (because using common functions), but for a client, DEFER and FAIL have the same meaning @@ -1703,18 +1817,22 @@ int tls_client_start(int fd, host_item *host, address_item *addr ARG_UNUSED, - void *v_ob) + transport_instance *tb +#ifdef EXPERIMENTAL_DANE + , dne_answer * unused_tlsa_dnsa +#endif + ) { -smtp_transport_options_block *ob = v_ob; +smtp_transport_options_block *ob = + (smtp_transport_options_block *)tb->options_block; int rc; const char *error; exim_gnutls_state_st *state = NULL; #ifndef DISABLE_OCSP -BOOL require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, - NULL, host->name, host->address, NULL) == OK; +BOOL require_ocsp = + verify_check_given_host(&ob->hosts_require_ocsp, host) == OK; BOOL request_ocsp = require_ocsp ? TRUE - : verify_check_this_host(&ob->hosts_request_ocsp, - NULL, host->name, host->address, NULL) == OK; + : verify_check_given_host(&ob->hosts_request_ocsp, host) == OK; #endif DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd); @@ -1745,39 +1863,22 @@ set but both tls_verify_hosts and tls_try_verify_hosts are unset. Check only the specified host patterns if one of them is defined */ -if (( state->exp_tls_verify_certificates - && !ob->tls_verify_hosts - && !ob->tls_try_verify_hosts - ) - || - verify_check_host(&ob->tls_verify_hosts) == OK +if ( ( state->exp_tls_verify_certificates + && !ob->tls_verify_hosts + && (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts) + ) + || verify_check_given_host(&ob->tls_verify_hosts, host) == OK ) { -#ifdef EXPERIMENTAL_CERTNAMES - if (ob->tls_verify_cert_hostnames) - { - DEBUG(D_tls) - debug_printf("TLS: server cert incl. hostname verification required.\n"); - state->verify_requirement = VERIFY_WITHHOST; - if (!expand_check(ob->tls_verify_cert_hostnames, - US"tls_verify_cert_hostnames", - &state->exp_tls_verify_cert_hostnames)) - return FAIL; - if (state->exp_tls_verify_cert_hostnames) - DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", - state->exp_tls_verify_cert_hostnames); - } - else -#endif - { - DEBUG(D_tls) - debug_printf("TLS: server certificate verification required.\n"); - state->verify_requirement = VERIFY_REQUIRED; - } + tls_client_setup_hostname_checks(host, state, ob); + DEBUG(D_tls) + debug_printf("TLS: server certificate verification required.\n"); + state->verify_requirement = VERIFY_REQUIRED; gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_REQUIRE); } -else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) +else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK) { + tls_client_setup_hostname_checks(host, state, ob); DEBUG(D_tls) debug_printf("TLS: server certificate verification optional.\n"); state->verify_requirement = VERIFY_OPTIONAL; @@ -1804,6 +1905,15 @@ } #endif +#ifdef EXPERIMENTAL_EVENT +if (tb->event_action) + { + state->event_action = tb->event_action; + gnutls_session_set_ptr(state->session, state); + gnutls_certificate_set_verify_function(state->x509_cred, verify_cb); + } +#endif + gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)(long) fd); state->fd_in = fd; state->fd_out = fd; diff -Nru exim4-4.84/src/tls-openssl.c exim4-4.86~RC4/src/tls-openssl.c --- exim4-4.84/src/tls-openssl.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/tls-openssl.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Portions Copyright (c) The OpenSSL Project 1999 */ @@ -22,9 +22,16 @@ #include #include #include +#ifndef OPENSSL_NO_ECDH +# include +#endif #ifndef DISABLE_OCSP # include #endif +#ifdef EXPERIMENTAL_DANE +# include +#endif + #ifndef DISABLE_OCSP # define EXIM_OCSP_SKEW_SECONDS (300L) @@ -35,6 +42,38 @@ # define EXIM_HAVE_OPENSSL_TLSEXT #endif +/* + * X509_check_host provides sane certificate hostname checking, but was added + * to OpenSSL late, after other projects forked off the code-base. So in + * addition to guarding against the base version number, beware that LibreSSL + * does not (at this time) support this function. + * + * If LibreSSL gains a different API, perhaps via libtls, then we'll probably + * opt to disentangle and ask a LibreSSL user to provide glue for a third + * crypto provider for libtls instead of continuing to tie the OpenSSL glue + * into even twistier knots. If LibreSSL gains the same API, we can just + * change this guard and punt the issue for a while longer. + */ +#ifndef LIBRESSL_VERSION_NUMBER +# if OPENSSL_VERSION_NUMBER >= 0x010100000L +# define EXIM_HAVE_OPENSSL_CHECKHOST +# endif +# if OPENSSL_VERSION_NUMBER >= 0x010000000L \ + && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L +# define EXIM_HAVE_OPENSSL_CHECKHOST +# endif + +# if !defined(OPENSSL_NO_ECDH) +# if OPENSSL_VERSION_NUMBER >= 0x0090800fL +# define EXIM_HAVE_ECDH +# endif +# if OPENSSL_VERSION_NUMBER >= 0x10002000L +# define EXIM_HAVE_OPENSSL_ECDH_AUTO +# define EXIM_HAVE_OPENSSL_EC_NIST2NID +# endif +# endif +#endif + #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP) # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile" # define DISABLE_OCSP @@ -112,9 +151,9 @@ uschar *server_cipher_list; /* only passed down to tls_error: */ host_item *host; - -#ifdef EXPERIMENTAL_CERTNAMES - uschar * verify_cert_hostnames; + const uschar * verify_cert_hostnames; +#ifdef EXPERIMENTAL_EVENT + uschar * event_action; #endif } tls_ext_ctx_cb; @@ -158,29 +197,30 @@ */ static int -tls_error(uschar *prefix, host_item *host, uschar *msg) +tls_error(uschar * prefix, const host_item * host, uschar * msg) { -if (msg == NULL) +if (!msg) { ERR_error_string(ERR_get_error(), ssl_errstring); msg = (uschar *)ssl_errstring; } -if (host == NULL) +if (host) + { + log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s): %s", + host->name, host->address, prefix, msg); + return FAIL; + } +else { uschar *conn_info = smtp_get_connection_info(); if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; + /* I'd like to get separated H= here, but too hard for now */ log_write(0, LOG_MAIN, "TLS error on %s (%s): %s", conn_info, prefix, msg); return DEFER; } -else - { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s): %s", - host->name, host->address, prefix, msg); - return FAIL; - } } @@ -233,6 +273,7 @@ { X509 * current_cert= tmp_obj->data.x509; X509_NAME_oneline(X509_get_subject_name(current_cert), CS name, sizeof(name)); + name[sizeof(name)-1] = '\0'; debug_printf(" %s\n", name); } } @@ -241,27 +282,67 @@ */ +#ifdef EXPERIMENTAL_EVENT +static int +verify_event(tls_support * tlsp, X509 * cert, int depth, const uschar * dn, + BOOL *calledp, const BOOL *optionalp, const uschar * what) +{ +uschar * ev; +uschar * yield; +X509 * old_cert; + +ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action; +if (ev) + { + old_cert = tlsp->peercert; + tlsp->peercert = X509_dup(cert); + /* NB we do not bother setting peerdn */ + if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth)))) + { + log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: " + "depth=%d cert=%s: %s", + tlsp == &tls_out ? deliver_host_address : sender_host_address, + what, depth, dn, yield); + *calledp = TRUE; + if (!*optionalp) + { + if (old_cert) tlsp->peercert = old_cert; /* restore 1st failing cert */ + return 1; /* reject (leaving peercert set) */ + } + DEBUG(D_tls) debug_printf("Event-action verify failure overridden " + "(host in tls_try_verify_hosts)\n"); + } + X509_free(tlsp->peercert); + tlsp->peercert = old_cert; + } +return 0; +} +#endif + /************************************************* * Callback for verification * *************************************************/ /* The SSL library does certificate verification if set up to do so. This callback has the current yes/no state is in "state". If verification succeeded, -we set up the tls_peerdn string. If verification failed, what happens depends -on whether the client is required to present a verifiable certificate or not. +we set the certificate-verified flag. If verification failed, what happens +depends on whether the client is required to present a verifiable certificate +or not. If verification is optional, we change the state to yes, but still log the verification error. For some reason (it really would help to have proper documentation of OpenSSL), this callback function then gets called again, this -time with state = 1. In fact, that's useful, because we can set up the peerdn -value, but we must take care not to set the private verified flag on the second -time through. +time with state = 1. We must take care not to set the private verified flag on +the second time through. Note: this function is not called if the client fails to present a certificate when asked. We get here only if a certificate has been received. Handling of optional verification for this case is done when requesting SSL to verify, by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case. +May be called multiple times for different issues with a certificate, even +for a given "depth" in the certificate chain. + Arguments: state current yes/no state as 1/0 x509ctx certificate information. @@ -275,31 +356,33 @@ tls_support *tlsp, BOOL *calledp, BOOL *optionalp) { X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); -static uschar txt[256]; +int depth = X509_STORE_CTX_get_error_depth(x509ctx); +uschar dn[256]; -X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt)); +X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)); +dn[sizeof(dn)-1] = '\0'; if (state == 0) { - log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s", - X509_STORE_CTX_get_error_depth(x509ctx), + log_write(0, LOG_MAIN, "[%s] SSL verify error: depth=%d error=%s cert=%s", + tlsp == &tls_out ? deliver_host_address : sender_host_address, + depth, X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)), - txt); - tlsp->certificate_verified = FALSE; + dn); *calledp = TRUE; if (!*optionalp) { - tlsp->peercert = X509_dup(cert); - return 0; /* reject */ + if (!tlsp->peercert) + tlsp->peercert = X509_dup(cert); /* record failing cert */ + return 0; /* reject */ } DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " "tls_try_verify_hosts)\n"); } -else if (X509_STORE_CTX_get_error_depth(x509ctx) != 0) +else if (depth != 0) { - DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", - X509_STORE_CTX_get_error_depth(x509ctx), txt); + DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, dn); #ifndef DISABLE_OCSP if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store) { /* client, wanting stapling */ @@ -311,65 +394,78 @@ ERR_clear_error(); } #endif +#ifdef EXPERIMENTAL_EVENT + if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL")) + return 0; /* reject, with peercert set */ +#endif } else { -#ifdef EXPERIMENTAL_CERTNAMES - uschar * verify_cert_hostnames; -#endif - - tlsp->peerdn = txt; - tlsp->peercert = X509_dup(cert); + const uschar * verify_cert_hostnames; -#ifdef EXPERIMENTAL_CERTNAMES if ( tlsp == &tls_out && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames))) /* client, wanting hostname check */ - -# if OPENSSL_VERSION_NUMBER >= 0x010100000L || OPENSSL_VERSION_NUMBER >= 0x010002000L -# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS -# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 -# endif { + +#ifdef EXIM_HAVE_OPENSSL_CHECKHOST +# ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS +# define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0 +# endif +# ifndef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS +# define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0 +# endif int sep = 0; - uschar * list = verify_cert_hostnames; + const uschar * list = verify_cert_hostnames; uschar * name; int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) if ((rc = X509_check_host(cert, name, 0, - X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS))) + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS + | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, + NULL))) { if (rc < 0) { - log_write(0, LOG_MAIN, "SSL verify error: internal error\n"); + log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error", + tlsp == &tls_out ? deliver_host_address : sender_host_address); name = NULL; } break; } if (!name) - { - log_write(0, LOG_MAIN, - "SSL verify error: certificate name mismatch: \"%s\"\n", txt); - return 0; /* reject */ - } - } -# else +#else if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) +#endif { log_write(0, LOG_MAIN, - "SSL verify error: certificate name mismatch: \"%s\"\n", txt); - return 0; /* reject */ + "[%s] SSL verify error: certificate name mismatch: \"%s\"", + tlsp == &tls_out ? deliver_host_address : sender_host_address, + dn); + *calledp = TRUE; + if (!*optionalp) + { + if (!tlsp->peercert) + tlsp->peercert = X509_dup(cert); /* record failing cert */ + return 0; /* reject */ + } + DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in " + "tls_try_verify_hosts)\n"); } -# endif + } + +#ifdef EXPERIMENTAL_EVENT + if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL")) + return 0; /* reject, with peercert set */ #endif DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n", - *calledp ? "" : " authenticated", txt); + *calledp ? "" : " authenticated", dn); if (!*calledp) tlsp->certificate_verified = TRUE; *calledp = TRUE; } -return 1; /* accept */ +return 1; /* accept, at least for this level */ } static int @@ -385,6 +481,40 @@ } +#ifdef EXPERIMENTAL_DANE + +/* This gets called *by* the dane library verify callback, which interposes +itself. +*/ +static int +verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx) +{ +X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx); +uschar dn[256]; +#ifdef EXPERIMENTAL_EVENT +int depth = X509_STORE_CTX_get_error_depth(x509ctx); +BOOL dummy_called, optional = FALSE; +#endif + +X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)); +dn[sizeof(dn)-1] = '\0'; + +DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", dn); + +#ifdef EXPERIMENTAL_EVENT + if (verify_event(&tls_out, cert, depth, dn, + &dummy_called, &optional, US"DANE")) + return 0; /* reject, with peercert set */ +#endif + +if (state == 1) + tls_out.dane_verified = + tls_out.certificate_verified = TRUE; +return 1; +} + +#endif /*EXPERIMENTAL_DANE*/ + /************************************************* * Information callback * @@ -419,6 +549,7 @@ /* If dhparam is set, expand it, and load up the parameters for DH encryption. Arguments: + sctx The current SSL CTX (inbound or outbound) dhparam DH parameter file or fixed parameter identity string host connected host, if client; NULL if server @@ -426,7 +557,7 @@ */ static BOOL -init_dh(SSL_CTX *sctx, uschar *dhparam, host_item *host) +init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host) { BIO *bio; DH *dh; @@ -498,6 +629,107 @@ +/************************************************* +* Initialize for ECDH * +*************************************************/ + +/* Load parameters for ECDH encryption. + +For now, we stick to NIST P-256 because: it's simple and easy to configure; +it avoids any patent issues that might bite redistributors; despite events in +the news and concerns over curve choices, we're not cryptographers, we're not +pretending to be, and this is "good enough" to be better than no support, +protecting against most adversaries. Given another year or two, there might +be sufficient clarity about a "right" way forward to let us make an informed +decision, instead of a knee-jerk reaction. + +Longer-term, we should look at supporting both various named curves and +external files generated with "openssl ecparam", much as we do for init_dh(). +We should also support "none" as a value, to explicitly avoid initialisation. + +Patches welcome. + +Arguments: + sctx The current SSL CTX (inbound or outbound) + host connected host, if client; NULL if server + +Returns: TRUE if OK (nothing to set up, or setup worked) +*/ + +static BOOL +init_ecdh(SSL_CTX * sctx, host_item * host) +{ +EC_KEY * ecdh; +uschar * exp_curve; +int nid; +BOOL rv; + +#ifdef OPENSSL_NO_ECDH +return TRUE; +#else + +if (host) /* No ECDH setup for clients, only for servers */ + return TRUE; + +# ifndef EXIM_HAVE_ECDH +DEBUG(D_tls) + debug_printf("No OpenSSL API to define ECDH parameters, skipping\n"); +return TRUE; +# else + +if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve)) + return FALSE; +if (!exp_curve || !*exp_curve) + return TRUE; + +# ifdef EXIM_HAVE_OPENSSL_ECDH_AUTO +/* check if new enough library to support auto ECDH temp key parameter selection */ +if (Ustrcmp(exp_curve, "auto") == 0) + { + DEBUG(D_tls) debug_printf( + "ECDH temp key parameter settings: OpenSSL 1.2+ autoselection\n"); + SSL_CTX_set_ecdh_auto(sctx, 1); + return TRUE; + } +# endif + +DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve); +if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef +# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID + && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef +# endif + ) + { + tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", + exp_curve), + host, NULL); + return FALSE; + } + +if (!(ecdh = EC_KEY_new_by_curve_name(nid))) + { + tls_error("Unable to create ec curve", host, NULL); + return FALSE; + } + +/* The "tmp" in the name here refers to setting a temporary key +not to the stability of the interface. */ + +if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) + tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL); +else + DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve); + +EC_KEY_free(ecdh); +return !rv; + +# endif /*EXIM_HAVE_ECDH*/ +#endif /*OPENSSL_NO_ECDH*/ +} + + + + #ifndef DISABLE_OCSP /************************************************* * Load OCSP information into state * @@ -781,6 +1013,12 @@ SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(server_ctx)); SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo); + +if ( !init_dh(server_sni, cbinfo->dhparam, NULL) + || !init_ecdh(server_sni, NULL) + ) + return SSL_TLSEXT_ERR_NOACK; + if (cbinfo->server_cipher_list) SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list); #ifndef DISABLE_OCSP @@ -796,10 +1034,7 @@ /* do this after setup_certs, because this can require the certs for verifying OCSP information. */ -rc = tls_expand_session_files(server_sni, cbinfo); -if (rc != OK) return SSL_TLSEXT_ERR_NOACK; - -if (!init_dh(server_sni, cbinfo->dhparam, NULL)) +if ((rc = tls_expand_session_files(server_sni, cbinfo)) != OK) return SSL_TLSEXT_ERR_NOACK; DEBUG(D_tls) debug_printf("Switching SSL context.\n"); @@ -834,7 +1069,7 @@ int response_der_len; DEBUG(D_tls) - debug_printf("Received TLS status request (OCSP stapling); %s response.", + debug_printf("Received TLS status request (OCSP stapling); %s response\n", cbinfo->u_ocsp.server.response ? "have" : "lack"); tls_in.ocsp = OCSP_NOT_RESP; @@ -888,7 +1123,7 @@ { tls_out.ocsp = OCSP_FAILED; if (log_extra_selector & LX_tls_cipher) - log_write(0, LOG_MAIN, "Received TLS status response, parse error"); + log_write(0, LOG_MAIN, "Received TLS cert status response, parse error"); else DEBUG(D_tls) debug_printf(" parse error\n"); return 0; @@ -898,7 +1133,7 @@ { tls_out.ocsp = OCSP_FAILED; if (log_extra_selector & LX_tls_cipher) - log_write(0, LOG_MAIN, "Received TLS status response, error parsing response"); + log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response"); else DEBUG(D_tls) debug_printf(" error parsing response\n"); OCSP_RESPONSE_free(rsp); @@ -928,6 +1163,8 @@ cbinfo->u_ocsp.client.verify_store, 0)) <= 0) { tls_out.ocsp = OCSP_FAILED; + if (log_extra_selector & LX_tls_cipher) + log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable"); BIO_printf(bp, "OCSP response verify failure\n"); ERR_print_errors(bp); i = cbinfo->u_ocsp.client.verify_required ? 0 : 1; @@ -999,7 +1236,6 @@ #endif /*!DISABLE_OCSP*/ - /************************************************* * Initialize for TLS * *************************************************/ @@ -1008,13 +1244,14 @@ of the library. We allocate and return a context structure. Arguments: + ctxp returned SSL context host connected host, if client; NULL if server dhparam DH parameter file certificate certificate file privatekey private key ocsp_file file of stapling info (server); flag for require ocsp (client) addr address if client; NULL if server (for some randomness) - cbp place to put allocated context + cbp place to put allocated callback context Returns: OK/DEFER/FAIL */ @@ -1030,7 +1267,7 @@ long init_options; int rc; BOOL okay; -tls_ext_ctx_cb *cbinfo; +tls_ext_ctx_cb * cbinfo; cbinfo = store_malloc(sizeof(tls_ext_ctx_cb)); cbinfo->certificate = certificate; @@ -1048,6 +1285,9 @@ cbinfo->dhparam = dhparam; cbinfo->server_cipher_list = NULL; cbinfo->host = host; +#ifdef EXPERIMENTAL_EVENT +cbinfo->event_action = NULL; +#endif SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); @@ -1097,7 +1337,7 @@ /* Set up the information callback, which outputs if debugging is at a suitable level. */ -SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); +DEBUG(D_tls) SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback); /* Automatically re-try reads/writes after renegotiation. */ (void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY); @@ -1126,8 +1366,12 @@ DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); /* Initialize with DH parameters if supplied */ +/* Initialize ECDH temp key parameter selection */ -if (!init_dh(*ctxp, dhparam, host)) return DEFER; +if ( !init_dh(*ctxp, dhparam, host) + || !init_ecdh(*ctxp, host) + ) + return DEFER; /* Set up certificate and key (and perhaps OCSP info) */ @@ -1169,9 +1413,7 @@ # endif #endif -#ifdef EXPERIMENTAL_CERTNAMES cbinfo->verify_cert_hostnames = NULL; -#endif /* Set up the RSA callback */ @@ -1223,6 +1465,29 @@ } +static void +peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned bsize) +{ +/*XXX we might consider a list-of-certs variable for the cert chain. +SSL_get_peer_cert_chain(SSL*). We'd need a new variable type and support +in list-handling functions, also consider the difference between the entire +chain and the elements sent by the peer. */ + +/* Will have already noted peercert on a verify fail; possibly not the leaf */ +if (!tlsp->peercert) + tlsp->peercert = SSL_get_peer_certificate(ssl); +/* Beware anonymous ciphers which lead to server_cert being NULL */ +if (tlsp->peercert) + { + X509_NAME_oneline(X509_get_subject_name(tlsp->peercert), CS peerdn, bsize); + peerdn[bsize-1] = '\0'; + tlsp->peerdn = peerdn; /*XXX a static buffer... */ + } +else + tlsp->peerdn = NULL; +} + + @@ -1255,36 +1520,65 @@ if (expcerts != NULL && *expcerts != '\0') { - struct stat statbuf; - if (!SSL_CTX_set_default_verify_paths(sctx)) - return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - - if (Ustat(expcerts, &statbuf) < 0) + if (Ustrcmp(expcerts, "system") == 0) { - log_write(0, LOG_MAIN|LOG_PANIC, - "failed to stat %s for certificates", expcerts); - return DEFER; + /* Tell the library to use its compiled-in location for the system default + CA bundle, only */ + + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); } else { - uschar *file, *dir; - if ((statbuf.st_mode & S_IFMT) == S_IFDIR) - { file = NULL; dir = expcerts; } - else - { file = expcerts; dir = NULL; } + struct stat statbuf; + + /* Tell the library to use its compiled-in location for the system default + CA bundle. Those given by the exim config are additional to these */ - /* If a certificate file is empty, the next function fails with an - unhelpful error message. If we skip it, we get the correct behaviour (no - certificates are recognized, but the error message is still misleading (it - says no certificate was supplied.) But this is better. */ - - if ((file == NULL || statbuf.st_size > 0) && - !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) - return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); - if (file != NULL) + if (Ustat(expcerts, &statbuf) < 0) { - SSL_CTX_set_client_CA_list(sctx, SSL_load_client_CA_file(CS file)); + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to stat %s for certificates", expcerts); + return DEFER; + } + else + { + uschar *file, *dir; + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { file = NULL; dir = expcerts; } + else + { file = expcerts; dir = NULL; } + + /* If a certificate file is empty, the next function fails with an + unhelpful error message. If we skip it, we get the correct behaviour (no + certificates are recognized, but the error message is still misleading (it + says no certificate was supplied.) But this is better. */ + + if ((file == NULL || statbuf.st_size > 0) && + !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) + return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); + + /* Load the list of CAs for which we will accept certs, for sending + to the client. This is only for the one-file tls_verify_certificates + variant. + If a list isn't loaded into the server, but + some verify locations are set, the server end appears to make + a wildcard reqest for client certs. + Meanwhile, the client library as deafult behaviour *ignores* the list + we send over the wire - see man SSL_CTX_set_client_cert_cb. + Because of this, and that the dir variant is likely only used for + the public-CA bundle (not for a private CA), not worth fixing. + */ + if (file != NULL) + { + STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); + DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", + sk_X509_NAME_num(names)); + SSL_CTX_set_client_CA_list(sctx, names); + } } } @@ -1376,6 +1670,7 @@ int rc; uschar *expciphers; tls_ext_ctx_cb *cbinfo; +static uschar peerdn[256]; static uschar cipherbuf[256]; /* Check for previous activation */ @@ -1420,6 +1715,9 @@ optional, set up appropriately. */ tls_in.certificate_verified = FALSE; +#ifdef EXPERIMENTAL_DANE +tls_in.dane_verified = FALSE; +#endif server_verify_callback_called = FALSE; if (verify_check_host(&tls_verify_hosts) == OK) @@ -1495,6 +1793,8 @@ /* TLS has been set up. Adjust the input functions to read via TLS, and initialize things. */ +peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn)); + construct_cipher_name(server_ssl, cipherbuf, sizeof(cipherbuf), &tls_in.bits); tls_in.cipher = cipherbuf; @@ -1533,6 +1833,104 @@ +static int +tls_client_basic_ctx_init(SSL_CTX * ctx, + host_item * host, smtp_transport_options_block * ob, tls_ext_ctx_cb * cbinfo + ) +{ +int rc; +/* stick to the old behaviour for compatibility if tls_verify_certificates is + set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only + the specified host patterns if one of them is defined */ + +if ( ( !ob->tls_verify_hosts + && (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts) + ) + || (verify_check_given_host(&ob->tls_verify_hosts, host) == OK) + ) + client_verify_optional = FALSE; +else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK) + client_verify_optional = TRUE; +else + return OK; + +if ((rc = setup_certs(ctx, ob->tls_verify_certificates, + ob->tls_crl, host, client_verify_optional, verify_callback_client)) != OK) + return rc; + +if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK) + { + cbinfo->verify_cert_hostnames = +#ifdef EXPERIMENTAL_INTERNATIONAL + string_domain_utf8_to_alabel(host->name, NULL); +#else + host->name; +#endif + DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", + cbinfo->verify_cert_hostnames); + } +return OK; +} + + +#ifdef EXPERIMENTAL_DANE +static int +dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa) +{ +dns_record * rr; +dns_scan dnss; +const char * hostnames[2] = { CS host->name, NULL }; +int found = 0; + +if (DANESSL_init(ssl, NULL, hostnames) != 1) + return tls_error(US"hostnames load", host, NULL); + +for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) + ) if (rr->type == T_TLSA) + { + uschar * p = rr->data; + uint8_t usage, selector, mtype; + const char * mdname; + + usage = *p++; + + /* Only DANE-TA(2) and DANE-EE(3) are supported */ + if (usage != 2 && usage != 3) continue; + + selector = *p++; + mtype = *p++; + + switch (mtype) + { + default: continue; /* Only match-types 0, 1, 2 are supported */ + case 0: mdname = NULL; break; + case 1: mdname = "sha256"; break; + case 2: mdname = "sha512"; break; + } + + found++; + switch (DANESSL_add_tlsa(ssl, usage, selector, mdname, p, rr->size - 3)) + { + default: + case 0: /* action not taken */ + return tls_error(US"tlsa load", host, NULL); + case 1: break; + } + + tls_out.tlsa_usage |= 1<options_block; +static uschar peerdn[256]; +uschar * expciphers; int rc; static uschar cipherbuf[256]; + #ifndef DISABLE_OCSP -BOOL require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp, - NULL, host->name, host->address, NULL) == OK; -BOOL request_ocsp = require_ocsp ? TRUE - : verify_check_this_host(&ob->hosts_request_ocsp, - NULL, host->name, host->address, NULL) == OK; +BOOL request_ocsp = FALSE; +BOOL require_ocsp = FALSE; +#endif + +#ifdef EXPERIMENTAL_DANE +tls_out.tlsa_usage = 0; +#endif + +#ifndef DISABLE_OCSP + { +# ifdef EXPERIMENTAL_DANE + if ( tlsa_dnsa + && ob->hosts_request_ocsp[0] == '*' + && ob->hosts_request_ocsp[1] == '\0' + ) + { + /* Unchanged from default. Use a safer one under DANE */ + request_ocsp = TRUE; + ob->hosts_request_ocsp = US"${if or { {= {0}{$tls_out_tlsa_usage}} " + " {= {4}{$tls_out_tlsa_usage}} } " + " {*}{}}"; + } +# endif + + if ((require_ocsp = + verify_check_given_host(&ob->hosts_require_ocsp, host) == OK)) + request_ocsp = TRUE; + else +# ifdef EXPERIMENTAL_DANE + if (!request_ocsp) +# endif + request_ocsp = + verify_check_given_host(&ob->hosts_request_ocsp, host) == OK; + } #endif rc = tls_init(&client_ctx, host, NULL, @@ -1597,38 +2030,25 @@ return tls_error(US"SSL_CTX_set_cipher_list", host, NULL); } -/* stick to the old behaviour for compatibility if tls_verify_certificates is - set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only - the specified host patterns if one of them is defined */ - -if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) || - (verify_check_host(&ob->tls_verify_hosts) == OK)) +#ifdef EXPERIMENTAL_DANE +if (tlsa_dnsa) { - if ((rc = setup_certs(client_ctx, ob->tls_verify_certificates, - ob->tls_crl, host, FALSE, verify_callback_client)) != OK) - return rc; - client_verify_optional = FALSE; + SSL_CTX_set_verify(client_ctx, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + verify_callback_client_dane); + + if (!DANESSL_library_init()) + return tls_error(US"library init", host, NULL); + if (DANESSL_CTX_init(client_ctx) <= 0) + return tls_error(US"context init", host, NULL); + } +else -#ifdef EXPERIMENTAL_CERTNAMES - if (ob->tls_verify_cert_hostnames) - { - if (!expand_check(ob->tls_verify_cert_hostnames, - US"tls_verify_cert_hostnames", - &client_static_cbinfo->verify_cert_hostnames)) - return FAIL; - if (client_static_cbinfo->verify_cert_hostnames) - DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", - client_static_cbinfo->verify_cert_hostnames); - } #endif - } -else if (verify_check_host(&ob->tls_try_verify_hosts) == OK) - { - if ((rc = setup_certs(client_ctx, ob->tls_verify_certificates, - ob->tls_crl, host, TRUE, verify_callback_client)) != OK) + + if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob, client_static_cbinfo)) + != OK) return rc; - client_verify_optional = TRUE; - } if ((client_ssl = SSL_new(client_ctx)) == NULL) return tls_error(US"SSL_new", host, NULL); @@ -1659,9 +2079,32 @@ } } +#ifdef EXPERIMENTAL_DANE +if (tlsa_dnsa) + if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa)) != OK) + return rc; +#endif + #ifndef DISABLE_OCSP /* Request certificate status at connection-time. If the server does OCSP stapling we will get the callback (set in tls_init()) */ +# ifdef EXPERIMENTAL_DANE +if (request_ocsp) + { + const uschar * s; + if ( ((s = ob->hosts_require_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage")) + || ((s = ob->hosts_request_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage")) + ) + { /* Re-eval now $tls_out_tlsa_usage is populated. If + this means we avoid the OCSP request, we wasted the setup + cost in tls_init(). */ + require_ocsp = verify_check_given_host(&ob->hosts_require_ocsp, host) == OK; + request_ocsp = require_ocsp + || verify_check_given_host(&ob->hosts_request_ocsp, host) == OK; + } + } +# endif + if (request_ocsp) { SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp); @@ -1670,6 +2113,10 @@ } #endif +#ifdef EXPERIMENTAL_EVENT +client_static_cbinfo->event_action = tb->event_action; +#endif + /* There doesn't seem to be a built-in timeout on connection. */ DEBUG(D_tls) debug_printf("Calling SSL_connect\n"); @@ -1678,22 +2125,17 @@ rc = SSL_connect(client_ssl); alarm(0); +#ifdef EXPERIMENTAL_DANE +if (tlsa_dnsa) + DANESSL_cleanup(client_ssl); +#endif + if (rc <= 0) return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL); DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); -/* Beware anonymous ciphers which lead to server_cert being NULL */ -/*XXX server_cert is never freed... use X509_free() */ -server_cert = SSL_get_peer_certificate (client_ssl); -if (server_cert) - { - tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert), - CS txt, sizeof(txt)); - tls_out.peerdn = txt; /*XXX a static buffer... */ - } -else - tls_out.peerdn = NULL; +peer_cert(client_ssl, &tls_out, peerdn, sizeof(peerdn)); construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits); tls_out.cipher = cipherbuf; diff -Nru exim4-4.84/src/tod.c exim4-4.86~RC4/src/tod.c --- exim4-4.84/src/tod.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/tod.c 2015-06-27 17:01:28.000000000 +0200 @@ -60,7 +60,7 @@ struct timeval tv; gettimeofday(&tv, NULL); /* Unix epoch/usec format */ - (void) sprintf(CS timebuf, "%ld%06ld", tv.tv_sec, (long) tv.tv_usec ); + (void) sprintf(CS timebuf, TIME_T_FMT "%06ld", tv.tv_sec, (long) tv.tv_usec ); return timebuf; } diff -Nru exim4-4.84/src/transport.c exim4-4.86~RC4/src/transport.c --- exim4-4.84/src/transport.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transport.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* General functions concerned with transportation, and generic options for all @@ -66,6 +66,10 @@ (void *)offsetof(transport_instance, driver_name) }, { "envelope_to_add", opt_bool|opt_public, (void *)(offsetof(transport_instance, envelope_to_add)) }, +#ifdef EXPERIMENTAL_EVENT + { "event_action", opt_stringptr | opt_public, + (void *)offsetof(transport_instance, event_action) }, +#endif { "group", opt_expand_gid|opt_public, (void *)offsetof(transport_instance, gid) }, { "headers_add", opt_stringptr|opt_public|opt_rep_str, @@ -94,10 +98,6 @@ (void *)offsetof(transport_instance, shadow_condition) }, { "shadow_transport", opt_stringptr|opt_public, (void *)offsetof(transport_instance, shadow) }, -#ifdef EXPERIMENTAL_TPDA - { "tpda_delivery_action",opt_stringptr | opt_public, - (void *)offsetof(transport_instance, tpda_delivery_action) }, -#endif { "transport_filter", opt_stringptr|opt_public, (void *)offsetof(transport_instance, filter_command) }, { "transport_filter_timeout", opt_time|opt_public, @@ -628,23 +628,22 @@ were removed (e.g. Bcc). If remove_headers is not null, skip any headers that match any entries therein. It is a colon-sep list; expand the items separately and squash any empty ones. -Then check addr->p.remove_headers too, provided that addr is not NULL. */ +Then check addr->prop.remove_headers too, provided that addr is not NULL. */ for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) { int i; - uschar *list = remove_headers; + const uschar *list = remove_headers; BOOL include_header = TRUE; - for (i = 0; i < 2; i++) /* For remove_headers && addr->p.remove_headers */ + for (i = 0; i < 2; i++) /* For remove_headers && addr->prop.remove_headers */ { if (list) { int sep = ':'; /* This is specified as a colon-separated list */ uschar *s, *ss; - uschar buffer[128]; - while ((s = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) + while ((s = string_nextinlist(&list, &sep, NULL, 0))) { int len; @@ -662,7 +661,7 @@ } if (s != NULL) { include_header = FALSE; break; } } - if (addr != NULL) list = addr->p.remove_headers; + if (addr != NULL) list = addr->prop.remove_headers; } /* If this header is to be output, try to rewrite it if there are rewriting @@ -710,7 +709,7 @@ if (addr) { int i; - header_line *hprev = addr->p.extra_headers; + header_line *hprev = addr->prop.extra_headers; header_line *hnext; for (i = 0; i < 2; i++) { @@ -741,7 +740,7 @@ int sep = '\n'; uschar * s; - while ((s = string_nextinlist(&add_headers, &sep, NULL, 0))) + while ((s = string_nextinlist(CUSS &add_headers, &sep, NULL, 0))) if (!(s = expand_string(s))) { if (!expand_string_forcedfail) @@ -915,7 +914,7 @@ /* Then the message's headers. Don't write any that are flagged as "old"; that means they were rewritten, or are a record of envelope rewriting, or were removed (e.g. Bcc). If remove_headers is not null, skip any headers that - match any entries therein. Then check addr->p.remove_headers too, provided that + match any entries therein. Then check addr->prop.remove_headers too, provided that addr is not NULL. */ if (!transport_headers_send(addr, fd, add_headers, remove_headers, &write_chunk, use_crlf, rewrite_rules, rewrite_existflags)) @@ -1016,7 +1015,6 @@ int sread = 0; int wwritten = 0; uschar *dkim_signature = NULL; -off_t size = 0; /* If we can't sign, just call the original function. */ @@ -1104,12 +1102,6 @@ } } -/* Fetch file size */ -size = lseek(dkim_fd, 0, SEEK_END); - -/* Rewind file */ -lseek(dkim_fd, 0, SEEK_SET); - #ifdef HAVE_LINUX_SENDFILE /* We can use sendfile() to shove the file contents to the socket. However only if we don't use TLS, @@ -1117,8 +1109,13 @@ before the data finally hits the socket. */ if (tls_out.active != fd) { + off_t size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */ ssize_t copied = 0; off_t offset = 0; + + /* Rewind file */ + lseek(dkim_fd, 0, SEEK_SET); + while(copied >= 0 && offset < size) copied = sendfile(fd, dkim_fd, &offset, size - offset); if (copied < 0) @@ -1126,42 +1123,47 @@ save_errno = errno; rc = FALSE; } - goto CLEANUP; } +else + #endif -/* Send file down the original fd */ -while((sread = read(dkim_fd, sbuf, 2048)) > 0) { - char *p = sbuf; - /* write the chunk */ + /* Rewind file */ + lseek(dkim_fd, 0, SEEK_SET); - while (sread) + /* Send file down the original fd */ + while((sread = read(dkim_fd, sbuf, 2048)) > 0) { + char *p = sbuf; + /* write the chunk */ + + while (sread) + { #ifdef SUPPORT_TLS - wwritten = tls_out.active == fd - ? tls_write(FALSE, US p, sread) - : write(fd, p, sread); + wwritten = tls_out.active == fd + ? tls_write(FALSE, US p, sread) + : write(fd, p, sread); #else - wwritten = write(fd, p, sread); + wwritten = write(fd, p, sread); #endif - if (wwritten == -1) - { - /* error, bail out */ - save_errno = errno; - rc = FALSE; - goto CLEANUP; + if (wwritten == -1) + { + /* error, bail out */ + save_errno = errno; + rc = FALSE; + goto CLEANUP; + } + p += wwritten; + sread -= wwritten; } - p += wwritten; - sread -= wwritten; } - } -if (sread == -1) - { - save_errno = errno; - rc = FALSE; - goto CLEANUP; + if (sread == -1) + { + save_errno = errno; + rc = FALSE; + } } CLEANUP: @@ -1243,8 +1245,8 @@ write_pid = (pid_t)(-1); (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); -filter_pid = child_open(transport_filter_argv, NULL, 077, &fd_write, &fd_read, - FALSE); +filter_pid = child_open(USS transport_filter_argv, NULL, 077, + &fd_write, &fd_read, FALSE); (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC); if (filter_pid < 0) goto TIDY_UP; /* errno set */ @@ -1482,7 +1484,7 @@ transport_update_waiting(host_item *hostlist, uschar *tpname) { uschar buffer[256]; -uschar *prevname = US""; +const uschar *prevname = US""; host_item *host; open_db dbblock; open_db *dbm_file; @@ -1623,20 +1625,39 @@ as set by the caller transport new_message_id set to the message id of a waiting message more set TRUE if there are yet more messages waiting + oicf_func function to call to validate if it is ok to send + to this message_id from the current instance. + oicf_data opaque data for oicf_func Returns: TRUE if new_message_id set; FALSE otherwise */ +typedef struct msgq_s +{ + uschar message_id [MESSAGE_ID_LENGTH + 1]; + BOOL bKeep; +} msgq_t; + BOOL -transport_check_waiting(uschar *transport_name, uschar *hostname, - int local_message_max, uschar *new_message_id, BOOL *more) +transport_check_waiting(const uschar *transport_name, const uschar *hostname, + int local_message_max, uschar *new_message_id, BOOL *more, oicf oicf_func, void *oicf_data) { dbdata_wait *host_record; -int host_length, path_len; +int host_length; open_db dbblock; open_db *dbm_file; uschar buffer[256]; +msgq_t *msgq = NULL; +int msgq_count = 0; +int msgq_actual = 0; +int i; +BOOL bFound = FALSE; +uschar spool_dir [PATH_MAX]; +uschar spool_file [PATH_MAX]; +struct stat statbuf; +BOOL bContinuation = FALSE; + *more = FALSE; DEBUG(D_transport) @@ -1689,58 +1710,107 @@ emptied, delete it and continue with any continuation records that may exist. */ -host_length = host_record->count * MESSAGE_ID_LENGTH; +/* For Bug 1141, I refactored this major portion of the routine, it is risky +but the 1 off will remain without it. This code now allows me to SKIP over +a message I do not want to send out on this run. */ -/* Loop to handle continuation host records in the database */ +sprintf(CS spool_dir, "%s/input/", spool_directory); -for (;;) +host_length = host_record->count * MESSAGE_ID_LENGTH; + +while (1) { - BOOL found = FALSE; + /* create an array to read entire message queue into memory for processing */ - sprintf(CS buffer, "%s/input/", spool_directory); - path_len = Ustrlen(buffer); + msgq = (msgq_t*) malloc(sizeof(msgq_t) * host_record->count); + msgq_count = host_record->count; + msgq_actual = msgq_count; - for (host_length -= MESSAGE_ID_LENGTH; host_length >= 0; - host_length -= MESSAGE_ID_LENGTH) + for (i = 0; i < host_record->count; ++i) { - struct stat statbuf; - Ustrncpy(new_message_id, host_record->text + host_length, + msgq[i].bKeep = TRUE; + + Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH); - new_message_id[MESSAGE_ID_LENGTH] = 0; + msgq[i].message_id[MESSAGE_ID_LENGTH] = 0; + } + /* first thing remove current message id if it exists */ + + for (i = 0; i < msgq_count; ++i) + if (Ustrcmp(msgq[i].message_id, message_id) == 0) + { + msgq[i].bKeep = FALSE; + break; + } + + /* now find the next acceptable message_id */ + + bFound = FALSE; + + for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep) + { if (split_spool_directory) - sprintf(CS(buffer + path_len), "%c/%s-D", new_message_id[5], new_message_id); + sprintf(CS spool_file, "%s%c/%s-D", + spool_dir, new_message_id[5], msgq[i].message_id); else - sprintf(CS(buffer + path_len), "%s-D", new_message_id); + sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id); - /* The listed message may be the one we are currently processing. If - so, we want to remove it from the list without doing anything else. - If not, do a stat to see if it is an existing message. If it is, break - the loop to handle it. No need to bother about locks; as this is all - "hint" processing, it won't matter if it doesn't exist by the time exim - actually tries to deliver it. */ + if (Ustat(spool_file, &statbuf) != 0) + msgq[i].bKeep = FALSE; + else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data)) + { + Ustrcpy(new_message_id, msgq[i].message_id); + msgq[i].bKeep = FALSE; + bFound = TRUE; + break; + } + } - if (Ustrcmp(new_message_id, message_id) != 0 && - Ustat(buffer, &statbuf) == 0) + /* re-count */ + for (msgq_actual = 0, i = 0; i < msgq_count; ++i) + if (msgq[i].bKeep) + msgq_actual++; + + /* reassemble the host record, based on removed message ids, from in + * memory queue. + */ + + if (msgq_actual <= 0) + { + host_length = 0; + host_record->count = 0; + } + else + { + host_length = msgq_actual * MESSAGE_ID_LENGTH; + host_record->count = msgq_actual; + + if (msgq_actual < msgq_count) { - found = TRUE; - break; + int new_count; + for (new_count = 0, i = 0; i < msgq_count; ++i) + if (msgq[i].bKeep) + Ustrncpy(&host_record->text[new_count++ * MESSAGE_ID_LENGTH], + msgq[i].message_id, MESSAGE_ID_LENGTH); + + host_record->text[new_count * MESSAGE_ID_LENGTH] = 0; } } - /* If we have removed all the message ids from the record delete the record. - If there is a continuation record, fetch it and remove it from the file, - as it will be rewritten as the main record. Repeat in the case of an - empty continuation. */ +/* Jeremy: check for a continuation record, this code I do not know how to +test but the code should work */ + + bContinuation = FALSE; while (host_length <= 0) { int i; - dbdata_wait *newr = NULL; + dbdata_wait * newr = NULL; /* Search for a continuation */ - for (i = host_record->sequence - 1; i >= 0 && newr == NULL; i--) + for (i = host_record->sequence - 1; i >= 0 && !newr; i--) { sprintf(CS buffer, "%.200s:%d", hostname, i); newr = dbfn_read(dbm_file, buffer); @@ -1748,7 +1818,7 @@ /* If no continuation, delete the current and break the loop */ - if (newr == NULL) + if (!newr) { dbfn_delete(dbm_file, hostname); break; @@ -1759,11 +1829,12 @@ dbfn_delete(dbm_file, buffer); host_record = newr; host_length = host_record->count * MESSAGE_ID_LENGTH; - } - /* If we found an existing message, break the continuation loop. */ + bContinuation = TRUE; + } - if (found) break; + if (bFound) + break; /* If host_length <= 0 we have emptied a record and not found a good message, and there are no continuation records. Otherwise there is a continuation @@ -1775,6 +1846,26 @@ DEBUG(D_transport) debug_printf("waiting messages already delivered\n"); return FALSE; } + + /* we were not able to find an acceptable message, nor was there a + * continuation record. So bug out, outer logic will clean this up. + */ + + if (!bContinuation) + { + Ustrcpy (new_message_id, message_id); + dbfn_close(dbm_file); + return FALSE; + } + } /* we need to process a continuation record */ + +/* clean up in memory queue */ +if (msgq) + { + free (msgq); + msgq = NULL; + msgq_count = 0; + msgq_actual = 0; } /* Control gets here when an existing message has been encountered; its @@ -1784,7 +1875,19 @@ if (host_length > 0) { + uschar msg [MESSAGE_ID_LENGTH + 1]; + int i; + host_record->count = host_length/MESSAGE_ID_LENGTH; + + /* rebuild the host_record->text */ + + for (i = 0; i < host_record->count; ++i) + { + Ustrncpy(msg, host_record->text + (i*MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH); + msg[MESSAGE_ID_LENGTH] = 0; + } + dbfn_write(dbm_file, hostname, host_record, (int)sizeof(dbdata_wait) + host_length); *more = TRUE; } @@ -1793,8 +1896,6 @@ return TRUE; } - - /************************************************* * Deliver waiting message down same socket * *************************************************/ @@ -1814,8 +1915,8 @@ */ BOOL -transport_pass_socket(uschar *transport_name, uschar *hostname, - uschar *hostaddress, uschar *id, int socket_fd) +transport_pass_socket(const uschar *transport_name, const uschar *hostname, + const uschar *hostaddress, uschar *id, int socket_fd) { pid_t pid; int status; @@ -1825,7 +1926,7 @@ if ((pid = fork()) == 0) { int i = 16; - uschar **argv; + const uschar **argv; /* Disconnect entirely from the parent process. If we are running in the test harness, wait for a bit to allow the previous process time to finish, @@ -1838,12 +1939,10 @@ /* Set up the calling arguments; use the standard function for the basics, but we have a number of extras that may be added. */ - argv = child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); + argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0); - #ifdef EXPERIMENTAL_DSN /* Call with the dsn flag */ if (smtp_use_dsn) argv[i++] = US"-MCD"; - #endif if (smtp_authenticated) argv[i++] = US"-MCA"; @@ -1862,9 +1961,9 @@ } argv[i++] = US"-MC"; - argv[i++] = transport_name; - argv[i++] = hostname; - argv[i++] = hostaddress; + argv[i++] = US transport_name; + argv[i++] = US hostname; + argv[i++] = US hostaddress; argv[i++] = string_sprintf("%d", continue_sequence + 1); argv[i++] = id; argv[i++] = NULL; @@ -1918,7 +2017,7 @@ Arguments: argvptr pointer to anchor for argv vector - cmd points to the command string + cmd points to the command string (modified IN PLACE) expand_arguments true if expansion is to occur expand_failed error value to set if expansion fails; not relevant if addr == NULL @@ -1932,11 +2031,12 @@ */ BOOL -transport_set_up_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_failed, address_item *addr, uschar *etext, uschar **errptr) +transport_set_up_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_failed, address_item *addr, + uschar *etext, uschar **errptr) { address_item *ad; -uschar **argv; +const uschar **argv; uschar *s, *ss; int address_count = 0; int argcount = 0; @@ -1970,7 +2070,7 @@ if (*s != 0) s++; *ss++ = 0; } - else argv[argcount++] = string_dequote(&s); + else argv[argcount++] = string_copy(string_dequote(CUSS &s)); while (isspace(*s)) s++; } @@ -2099,7 +2199,8 @@ if (*s != 0) s++; *ss++ = 0; } - else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s); + else address_pipe_argv[address_pipe_argcount++] = + string_copy(string_dequote(CUSS &s)); while (isspace(*s)) s++; /* strip space after arg */ } @@ -2167,9 +2268,9 @@ else { - uschar *expanded_arg; + const uschar *expanded_arg; enable_dollar_recipients = allow_dollar_recipients; - expanded_arg = expand_string(argv[i]); + expanded_arg = expand_cstring(argv[i]); enable_dollar_recipients = FALSE; if (expanded_arg == NULL) diff -Nru exim4-4.84/src/transports/appendfile.c exim4-4.86~RC4/src/transports/appendfile.c --- exim4-4.84/src/transports/appendfile.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/appendfile.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -664,7 +664,7 @@ transport_instance * check_file_format(int cfd, transport_instance *tblock, address_item *addr) { -uschar *format = +const uschar *format = ((appendfile_transport_options_block *)(tblock->options_block))->file_format; uschar data[256]; int len = read(cfd, data, sizeof(data)); @@ -2540,8 +2540,8 @@ uschar *basename; (void)gettimeofday(&msg_tv, NULL); - basename = string_sprintf("%lu.H%luP%lu.%s", msg_tv.tv_sec, - msg_tv.tv_usec, getpid(), primary_hostname); + basename = string_sprintf(TIME_T_FMT ".H%luP%lu.%s", + msg_tv.tv_sec, msg_tv.tv_usec, getpid(), primary_hostname); filename = dataname = string_sprintf("tmp/%s", basename); newname = string_sprintf("new/%s", basename); diff -Nru exim4-4.84/src/transports/autoreply.c exim4-4.86~RC4/src/transports/autoreply.c --- exim4-4.84/src/transports/autoreply.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/autoreply.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -157,12 +157,13 @@ if (type != cke_text) for (t = ss; *t != 0; t++) { int c = *t; + const uschar * sp; if (mac_isprint(c)) continue; if (type == cke_hdr && c == '\n' && (t[1] == ' ' || t[1] == '\t')) continue; - s = string_printing(s); + sp = string_printing(s); addr->transport_return = FAIL; addr->message = string_sprintf("Expansion of \"%s\" in %s transport " - "contains non-printing character %d", s, name, c); + "contains non-printing character %d", sp, name, c); return NULL; } @@ -187,7 +188,7 @@ */ static void -check_never_mail(uschar **listptr, uschar *never_mail) +check_never_mail(uschar **listptr, const uschar *never_mail) { uschar *s = *listptr; @@ -379,7 +380,7 @@ if (ob->never_mail != NULL) { - uschar *never_mail = expand_string(ob->never_mail); + const uschar *never_mail = expand_string(ob->never_mail); if (never_mail == NULL) { @@ -676,6 +677,7 @@ } else fprintf(f, "%s", CS big_buffer); } + (void) fclose(ff); } /* Copy the original message if required, observing the return size diff -Nru exim4-4.84/src/transports/lmtp.c exim4-4.86~RC4/src/transports/lmtp.c --- exim4-4.84/src/transports/lmtp.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/lmtp.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -171,7 +171,7 @@ if (buffer[0] != 0) { - uschar *s = string_printing(buffer); + const uschar *s = string_printing(buffer); *message = string_sprintf("LMTP error after %s: %s", big_buffer, s); *yield = buffer[0]; return TRUE; @@ -460,7 +460,7 @@ address_item *addr; uschar *igquotstr = US""; uschar *sockname = NULL; -uschar **argv; +const uschar **argv; uschar buffer[256]; DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name); @@ -469,13 +469,29 @@ not both. When a command is specified, call the common function for creating an argument list and expanding the items. */ -if (ob->cmd != NULL) +if (ob->cmd) { DEBUG(D_transport) debug_printf("using command %s\n", ob->cmd); sprintf(CS buffer, "%.50s transport", tblock->name); if (!transport_set_up_command(&argv, ob->cmd, TRUE, PANIC, addrlist, buffer, NULL)) return FALSE; + + /* If the -N option is set, can't do any more. Presume all has gone well. */ + if (dont_deliver) + goto MINUS_N; + +/* As this is a local transport, we are already running with the required +uid/gid and current directory. Request that the new process be a process group +leader, so we can kill it and all its children on an error. */ + + if ((pid = child_open(USS argv, NULL, 0, &fd_in, &fd_out, TRUE)) < 0) + { + addrlist->message = string_sprintf( + "Failed to create child process for %s transport: %s", tblock->name, + strerror(errno)); + return FALSE; + } } /* When a socket is specified, expand the string and create a socket. */ @@ -498,38 +514,11 @@ ob->skt, tblock->name, strerror(errno)); return FALSE; } - } -/* If the -N option is set, can't do any more. Presume all has gone well. */ + /* If the -N option is set, can't do any more. Presume all has gone well. */ + if (dont_deliver) + goto MINUS_N; -if (dont_deliver) - { - DEBUG(D_transport) - debug_printf("*** delivery by %s transport bypassed by -N option", - tblock->name); - addrlist->transport_return = OK; - return FALSE; - } - -/* As this is a local transport, we are already running with the required -uid/gid and current directory. Request that the new process be a process group -leader, so we can kill it and all its children on an error. */ - -if (ob->cmd != NULL) - { - if ((pid = child_open(argv, NULL, 0, &fd_in, &fd_out, TRUE)) < 0) - { - addrlist->message = string_sprintf( - "Failed to create child process for %s transport: %s", tblock->name, - strerror(errno)); - return FALSE; - } - } - -/* For a socket, try to make the connection */ - -else - { sockun.sun_family = AF_UNIX; sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname); if(connect(fd_out, (struct sockaddr *)(&sockun), sizeof(sockun)) == -1) @@ -541,6 +530,7 @@ } } + /* Make the output we are going to read into a file. */ out = fdopen(fd_out, "rb"); @@ -666,8 +656,9 @@ addr->transport_return = OK; if ((log_extra_selector & LX_smtp_confirmation) != 0) { - uschar *s = string_printing(buffer); - addr->message = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* de-const safe here as string_printing known to have alloc'n'copied */ + addr->message = (s == buffer)? (uschar *)string_copy(s) : US s; } } /* If the response has failed badly, use it for all the remaining pending @@ -784,6 +775,14 @@ debug_printf("%s transport yields %d\n", tblock->name, yield); return yield; + + +MINUS_N: + DEBUG(D_transport) + debug_printf("*** delivery by %s transport bypassed by -N option", + tblock->name); + addrlist->transport_return = OK; + return FALSE; } /* End of transport/lmtp.c */ diff -Nru exim4-4.84/src/transports/Makefile exim4-4.86~RC4/src/transports/Makefile --- exim4-4.84/src/transports/Makefile 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/Makefile 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ # calling it transports.a. This is called from the main make file, after cd'ing # to the transports subdirectory. -OBJ = appendfile.o autoreply.o lmtp.o pipe.o smtp.o tf_maildir.o +OBJ = appendfile.o autoreply.o lmtp.o pipe.o smtp.o smtp_socks.o tf_maildir.o transports.a: $(OBJ) @$(RM_COMMAND) -f transports.a @@ -19,6 +19,7 @@ lmtp.o: $(HDRS) lmtp.c lmtp.h pipe.o: $(HDRS) pipe.c pipe.h smtp.o: $(HDRS) smtp.c smtp.h +smtp_socks.o: $(HDRS) smtp_socks.c smtp.h tf_maildir.o: $(HDRS) tf_maildir.c tf_maildir.h appendfile.h diff -Nru exim4-4.84/src/transports/pipe.c exim4-4.86~RC4/src/transports/pipe.c --- exim4-4.84/src/transports/pipe.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/pipe.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -326,12 +326,12 @@ */ static BOOL -set_up_direct_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_fail, address_item *addr, uschar *tname, +set_up_direct_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname, pipe_transport_options_block *ob) { BOOL permitted = FALSE; -uschar **argv; +const uschar **argv; uschar buffer[64]; /* Set up "transport " to be put in any error messages, and then @@ -353,11 +353,11 @@ if (ob->allow_commands != NULL) { int sep = 0; - uschar *s, *p; + const uschar *s; + uschar *p; uschar buffer[256]; - s = expand_string(ob->allow_commands); - if (s == NULL) + if (!(s = expand_string(ob->allow_commands))) { addr->transport_return = DEFER; addr->message = string_sprintf("failed to expand string \"%s\" " @@ -365,10 +365,8 @@ return FALSE; } - while ((p = string_nextinlist(&s, &sep, buffer, sizeof(buffer))) != NULL) - { + while ((p = string_nextinlist(&s, &sep, buffer, sizeof(buffer)))) if (Ustrcmp(p, argv[0]) == 0) { permitted = TRUE; break; } - } } /* If permitted is TRUE it means the command was found in the allowed list, and @@ -407,7 +405,7 @@ { int sep = 0; uschar *p; - uschar *listptr = ob->path; + const uschar *listptr = ob->path; uschar buffer[1024]; while ((p = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) != NULL) @@ -453,10 +451,10 @@ */ static BOOL -set_up_shell_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, - int expand_fail, address_item *addr, uschar *tname) +set_up_shell_command(const uschar ***argvptr, uschar *cmd, + BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname) { -uschar **argv; +const uschar **argv; *argvptr = argv = store_get((4)*sizeof(uschar *)); @@ -551,9 +549,9 @@ int timeout = ob->timeout; BOOL written_ok = FALSE; BOOL expand_arguments; -uschar **argv; +const uschar **argv; uschar *envp[50]; -uschar *envlist = ob->environment; +const uschar *envlist = ob->environment; uschar *cmd, *ss; uschar *eol = (ob->use_crlf)? US"\r\n" : US"\n"; @@ -669,9 +667,9 @@ /* Add any requested items */ -if (envlist != NULL) +if (envlist) { - envlist = expand_string(envlist); + envlist = expand_cstring(envlist); if (envlist == NULL) { addr->transport_return = DEFER; @@ -729,7 +727,7 @@ uid/gid and current directory. Request that the new process be a process group leader, so we can kill it and all its children on a timeout. */ -if ((pid = child_open(argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) +if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) { addr->transport_return = DEFER; addr->message = string_sprintf( @@ -1073,16 +1071,14 @@ else { - uschar *s = ob->temp_errors; + const uschar *s = ob->temp_errors; uschar *p; uschar buffer[64]; int sep = 0; addr->transport_return = FAIL; - while ((p = string_nextinlist(&s,&sep,buffer,sizeof(buffer))) != NULL) - { + while ((p = string_nextinlist(&s,&sep,buffer,sizeof(buffer)))) if (rc == Uatoi(p)) { addr->transport_return = DEFER; break; } - } } /* Ensure the message contains the expanded command and arguments. This diff -Nru exim4-4.84/src/transports/smtp.c exim4-4.86~RC4/src/transports/smtp.c --- exim4-4.84/src/transports/smtp.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/smtp.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -19,6 +19,11 @@ to be publicly visible; these are flagged with opt_public. */ optionlist smtp_transport_options[] = { + { "*expand_multi_domain", opt_stringptr | opt_hidden | opt_public, + (void *)offsetof(transport_instance, expand_multi_domain) }, + { "*expand_retry_include_ip_address", opt_stringptr | opt_hidden, + (void *)(offsetof(smtp_transport_options_block, expand_retry_include_ip_address)) }, + { "address_retry_include_sender", opt_bool, (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) }, { "allow_localhost", opt_bool, @@ -56,9 +61,9 @@ { "dns_search_parents", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_search_parents) }, { "dnssec_request_domains", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dnssec_request_domains) }, + (void *)offsetof(smtp_transport_options_block, dnssec.request) }, { "dnssec_require_domains", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, dnssec_require_domains) }, + (void *)offsetof(smtp_transport_options_block, dnssec.require) }, { "dscp", opt_stringptr, (void *)offsetof(smtp_transport_options_block, dscp) }, { "fallback_hosts", opt_stringptr, @@ -109,6 +114,10 @@ { "hosts_require_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_auth) }, #ifdef SUPPORT_TLS +# ifdef EXPERIMENTAL_DANE + { "hosts_require_dane", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_require_dane) }, +# endif # ifndef DISABLE_OCSP { "hosts_require_ocsp", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_require_ocsp) }, @@ -118,6 +127,10 @@ #endif { "hosts_try_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_auth) }, +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { "hosts_try_dane", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_try_dane) }, +#endif #ifndef DISABLE_PRDR { "hosts_try_prdr", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_prdr) }, @@ -134,18 +147,22 @@ (void *)offsetof(smtp_transport_options_block, lmtp_ignore_quota) }, { "max_rcpt", opt_int | opt_public, (void *)offsetof(transport_instance, max_addresses) }, - { "multi_domain", opt_bool | opt_public, + { "multi_domain", opt_expand_bool | opt_public, (void *)offsetof(transport_instance, multi_domain) }, { "port", opt_stringptr, (void *)offsetof(smtp_transport_options_block, port) }, { "protocol", opt_stringptr, (void *)offsetof(smtp_transport_options_block, protocol) }, - { "retry_include_ip_address", opt_bool, + { "retry_include_ip_address", opt_expand_bool, (void *)offsetof(smtp_transport_options_block, retry_include_ip_address) }, { "serialize_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, serialize_hosts) }, { "size_addition", opt_int, (void *)offsetof(smtp_transport_options_block, size_addition) } +#ifdef EXPERIMENTAL_SOCKS + ,{ "socks_proxy", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, socks_proxy) } +#endif #ifdef SUPPORT_TLS ,{ "tls_certificate", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_certificate) }, @@ -163,19 +180,13 @@ (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) }, { "tls_try_verify_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) }, -#ifdef EXPERIMENTAL_CERTNAMES { "tls_verify_cert_hostnames", opt_stringptr, (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)}, -#endif { "tls_verify_certificates", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) }, { "tls_verify_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) } #endif -#ifdef EXPERIMENTAL_TPDA - ,{ "tpda_host_defer_action", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, tpda_host_defer_action) }, -#endif }; /* Size of the options list. An extern variable has to be used so that its @@ -200,16 +211,20 @@ NULL, /* serialize_hosts */ NULL, /* hosts_try_auth */ NULL, /* hosts_require_auth */ +#ifdef EXPERIMENTAL_DANE + NULL, /* hosts_try_dane */ + NULL, /* hosts_require_dane */ +#endif #ifndef DISABLE_PRDR - NULL, /* hosts_try_prdr */ + US"*", /* hosts_try_prdr */ #endif #ifndef DISABLE_OCSP - US"*", /* hosts_request_ocsp */ + US"*", /* hosts_request_ocsp (except under DANE; tls_client_start()) */ NULL, /* hosts_require_ocsp */ #endif NULL, /* hosts_require_tls */ NULL, /* hosts_avoid_tls */ - US"*", /* hosts_verify_avoid_tls */ + NULL, /* hosts_verify_avoid_tls */ NULL, /* hosts_avoid_pipelining */ NULL, /* hosts_avoid_esmtp */ NULL, /* hosts_nopass_tls */ @@ -226,14 +241,17 @@ FALSE, /* gethostbyname */ TRUE, /* dns_qualify_single */ FALSE, /* dns_search_parents */ - NULL, /* dnssec_request_domains */ - NULL, /* dnssec_require_domains */ + { NULL, NULL }, /* dnssec_domains {request,require} */ TRUE, /* delay_after_cutoff */ FALSE, /* hosts_override */ FALSE, /* hosts_randomize */ TRUE, /* keepalive */ FALSE, /* lmtp_ignore_quota */ + NULL, /* expand_retry_include_ip_address */ TRUE /* retry_include_ip_address */ +#ifdef EXPERIMENTAL_SOCKS + ,NULL /* socks_proxy */ +#endif #ifdef SUPPORT_TLS ,NULL, /* tls_certificate */ NULL, /* tls_crl */ @@ -243,15 +261,13 @@ NULL, /* gnutls_require_mac */ NULL, /* gnutls_require_proto */ NULL, /* tls_sni */ - NULL, /* tls_verify_certificates */ + US"system", /* tls_verify_certificates */ EXIM_CLIENT_DH_DEFAULT_MIN_BITS, /* tls_dh_min_bits */ TRUE, /* tls_tempfail_tryclear */ NULL, /* tls_verify_hosts */ - NULL /* tls_try_verify_hosts */ -# ifdef EXPERIMENTAL_CERTNAMES - ,NULL /* tls_verify_cert_hostnames */ -# endif + US"*", /* tls_try_verify_hosts */ + US"*" /* tls_verify_cert_hostnames */ #endif #ifndef DISABLE_DKIM ,NULL, /* dkim_canon */ @@ -261,19 +277,14 @@ NULL, /* dkim_sign_headers */ NULL /* dkim_strict */ #endif -#ifdef EXPERIMENTAL_TPDA - ,NULL /* tpda_host_defer_action */ -#endif }; -#ifdef EXPERIMENTAL_DSN /* some DSN flags for use later */ static int rf_list[] = {rf_notify_never, rf_notify_success, rf_notify_failure, rf_notify_delay }; -static uschar *rf_names[] = { "NEVER", "SUCCESS", "FAILURE", "DELAY" }; -#endif +static uschar *rf_names[] = { US"NEVER", US"SUCCESS", US"FAILURE", US"DELAY" }; @@ -428,6 +439,7 @@ msg to put in each address's message field rc to put in each address's transport_return field pass_message if TRUE, set the "pass message" flag in the address + host if set, mark addrs as having used this host If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate @@ -438,7 +450,7 @@ static void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc, - BOOL pass_message) + BOOL pass_message, host_item * host) { address_item *addr; int orvalue = 0; @@ -448,17 +460,19 @@ orvalue = RTEF_CTOUT; } for (addr = addrlist; addr != NULL; addr = addr->next) - { - if (addr->transport_return < PENDING) continue; - addr->basic_errno = errno_value; - addr->more_errno |= orvalue; - if (msg != NULL) + if (addr->transport_return >= PENDING) { - addr->message = msg; - if (pass_message) setflag(addr, af_pass_message); + addr->basic_errno = errno_value; + addr->more_errno |= orvalue; + if (msg != NULL) + { + addr->message = msg; + if (pass_message) setflag(addr, af_pass_message); + } + addr->transport_return = rc; + if (host) + addr->host_used = host; } - addr->transport_return = rc; - } } @@ -485,7 +499,8 @@ Returns: TRUE if an SMTP "QUIT" command should be sent, else FALSE */ -static BOOL check_response(host_item *host, int *errno_value, int more_errno, +static BOOL +check_response(host_item *host, int *errno_value, int more_errno, uschar *buffer, int *yield, uschar **message, BOOL *pass_message) { uschar *pl = US""; @@ -502,8 +517,8 @@ if (*errno_value == ETIMEDOUT) { - *message = US string_sprintf("SMTP timeout while connected to %s [%s] " - "after %s%s", host->name, host->address, pl, smtp_command); + *message = US string_sprintf("SMTP timeout after %s%s", + pl, smtp_command); if (transport_count > 0) *message = US string_sprintf("%s (%d bytes written)", *message, transport_count); @@ -514,15 +529,13 @@ if (*errno_value == ERRNO_SMTPFORMAT) { - uschar *malfresp = string_printing(buffer); + const uschar *malfresp = string_printing(buffer); while (isspace(*malfresp)) malfresp++; - if (*malfresp == 0) - *message = string_sprintf("Malformed SMTP reply (an empty line) from " - "%s [%s] in response to %s%s", host->name, host->address, pl, - smtp_command); - else - *message = string_sprintf("Malformed SMTP reply from %s [%s] in response " - "to %s%s: %s", host->name, host->address, pl, smtp_command, malfresp); + *message = *malfresp == 0 + ? string_sprintf("Malformed SMTP reply (an empty line) " + "in response to %s%s", pl, smtp_command) + : string_sprintf("Malformed SMTP reply in response to %s%s: %s", + pl, smtp_command, malfresp); return FALSE; } @@ -556,13 +569,23 @@ return FALSE; } +#ifdef EXPERIMENTAL_INTERNATIONAL +/* Handle lack of advertised SMTPUTF8, for international message */ +if (*errno_value == ERRNO_UTF8_FWD) + { + *message = US string_sprintf("utf8 support required but not offered for forwarding"); + DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message); + return TRUE; + } +#endif + /* Handle error responses from the remote mailer. */ if (buffer[0] != 0) { - uschar *s = string_printing(buffer); + const uschar *s = string_printing(buffer); *message = US string_sprintf("SMTP error from remote mail server after %s%s: " - "host %s [%s]: %s", pl, smtp_command, host->name, host->address, s); + "%s", pl, smtp_command, s); *pass_message = TRUE; *yield = buffer[0]; return TRUE; @@ -577,8 +600,8 @@ if (*errno_value == 0 || *errno_value == ECONNRESET) { *errno_value = ERRNO_SMTPCLOSED; - *message = US string_sprintf("Remote host %s [%s] closed connection " - "in response to %s%s", host->name, host->address, pl, smtp_command); + *message = US string_sprintf("Remote host closed connection " + "in response to %s%s", pl, smtp_command); } else *message = US string_sprintf("%s [%s]", host->name, host->address); @@ -603,9 +626,11 @@ static void write_logs(address_item *addr, host_item *host) { -if (addr->message != NULL) +uschar * message = string_sprintf("H=%s [%s]", host->name, host->address); + +if (addr->message) { - uschar *message = addr->message; + message = string_sprintf("%s: %s", message, addr->message); if (addr->basic_errno > 0) message = string_sprintf("%s: %s", message, strerror(addr->basic_errno)); log_write(0, LOG_MAIN, "%s", message); @@ -613,21 +638,25 @@ } else { - uschar *msg = - ((log_extra_selector & LX_outgoing_port) != 0)? - string_sprintf("%s [%s]:%d", host->name, host->address, - (host->port == PORT_NONE)? 25 : host->port) - : - string_sprintf("%s [%s]", host->name, host->address); - log_write(0, LOG_MAIN, "%s %s", msg, strerror(addr->basic_errno)); - deliver_msglog("%s %s %s\n", tod_stamp(tod_log), msg, - strerror(addr->basic_errno)); + if (log_extra_selector & LX_outgoing_port) + message = string_sprintf("%s:%d", message, + host->port == PORT_NONE ? 25 : host->port); + log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno)); + deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message, + strerror(addr->basic_errno)); } } +static void +msglog_line(host_item * host, uschar * message) +{ + deliver_msglog("%s H=%s [%s] %s\n", tod_stamp(tod_log), + host->name, host->address, message); +} + -#ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT /************************************************* * Post-defer action * *************************************************/ @@ -636,7 +665,6 @@ It might, for example, be used to write to the database log. Arguments: - ob transport options block addr the address item containing error information host the current host @@ -644,42 +672,43 @@ */ static void -tpda_deferred(smtp_transport_options_block *ob, address_item *addr, host_item *host) +deferred_event_raise(address_item *addr, host_item *host) { -uschar *action = ob->tpda_host_defer_action; +uschar * action = addr->transport->event_action; +const uschar * save_domain; +uschar * save_local; + if (!action) - return; + return; -tpda_delivery_ip = string_copy(host->address); -tpda_delivery_port = (host->port == PORT_NONE)? 25 : host->port; -tpda_delivery_fqdn = string_copy(host->name); -tpda_delivery_local_part = string_copy(addr->local_part); -tpda_delivery_domain = string_copy(addr->domain); -tpda_defer_errno = addr->basic_errno; - -tpda_defer_errstr = addr->message - ? addr->basic_errno > 0 - ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno)) - : string_copy(addr->message) - : addr->basic_errno > 0 - ? string_copy(US strerror(addr->basic_errno)) - : NULL; +save_domain = deliver_domain; +save_local = deliver_localpart; -DEBUG(D_transport) - debug_printf(" TPDA(host defer): tpda_host_defer_action=|%s| tpda_delivery_IP=%s\n", - action, tpda_delivery_ip); +/*XXX would ip & port already be set up? */ +deliver_host_address = string_copy(host->address); +deliver_host_port = host->port == PORT_NONE ? 25 : host->port; +event_defer_errno = addr->basic_errno; router_name = addr->router->name; transport_name = addr->transport->name; -if (!expand_string(action) && *expand_string_message) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand tpda_defer_action in %s: %s\n", - transport_name, expand_string_message); +deliver_domain = addr->domain; +deliver_localpart = addr->local_part; + +(void) event_raise(action, US"msg:host:defer", + addr->message + ? addr->basic_errno > 0 + ? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno)) + : string_copy(addr->message) + : addr->basic_errno > 0 + ? string_copy(US strerror(addr->basic_errno)) + : NULL); + +deliver_localpart = save_local; +deliver_domain = save_domain; router_name = transport_name = NULL; } #endif - - /************************************************* * Synchronize SMTP responses * *************************************************/ @@ -769,6 +798,14 @@ } errno = save_errno; } + + if (pending_DATA) count--; /* Number of RCPT responses to come */ + while (count-- > 0) /* Mark any pending addrs with the host used */ + { + while (addr->transport_return != PENDING_DEFER) addr = addr->next; + addr->host_used = host; + addr = addr->next; + } return -3; } } @@ -784,6 +821,7 @@ while (addr->transport_return != PENDING_DEFER) addr = addr->next; /* The address was accepted */ + addr->host_used = host; if (smtp_read_response(inblock, buffer, buffsize, '2', timeout)) { @@ -807,11 +845,9 @@ else if (errno == ETIMEDOUT) { - int save_errno = errno; - uschar *message = string_sprintf("SMTP timeout while connected to %s [%s] " - "after RCPT TO:<%s>", host->name, host->address, - transport_rcpt_address(addr, include_affixes)); - set_errno(addrlist, save_errno, message, DEFER, FALSE); + uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>", + transport_rcpt_address(addr, include_affixes)); + set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; return -1; @@ -835,10 +871,10 @@ { addr->message = string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: " - "host %s [%s]: %s", transport_rcpt_address(addr, include_affixes), - host->name, host->address, string_printing(buffer)); + "%s", transport_rcpt_address(addr, include_affixes), + string_printing(buffer)); setflag(addr, af_pass_message); - deliver_msglog("%s %s\n", tod_stamp(tod_log), addr->message); + msglog_line(host, addr->message); /* The response was 5xx */ @@ -856,9 +892,11 @@ addr->basic_errno = ERRNO_RCPT4XX; addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; - /* Log temporary errors if there are more hosts to be tried. */ + /* Log temporary errors if there are more hosts to be tried. + If not, log this last one in the == line. */ - if (host->next != NULL) log_write(0, LOG_MAIN, "%s", addr->message); + if (host->next) + log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message); /* Do not put this message on the list of those waiting for specific hosts, as otherwise it is likely to be tried too often. */ @@ -940,147 +978,146 @@ smtp_transport_options_block *ob, BOOL is_esmtp, smtp_inblock *ibp, smtp_outblock *obp) { - int require_auth; - uschar *fail_reason = US"server did not advertise AUTH support"; +int require_auth; +uschar *fail_reason = US"server did not advertise AUTH support"; - smtp_authenticated = FALSE; - client_authenticator = client_authenticated_id = client_authenticated_sender = NULL; - require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL, - host->name, host->address, NULL); +smtp_authenticated = FALSE; +client_authenticator = client_authenticated_id = client_authenticated_sender = NULL; +require_auth = verify_check_given_host(&ob->hosts_require_auth, host); - if (is_esmtp && !regex_AUTH) regex_AUTH = - regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", - FALSE, TRUE); +if (is_esmtp && !regex_AUTH) regex_AUTH = + regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", + FALSE, TRUE); - if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) - { - uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]); - expand_nmax = -1; /* reset */ +if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) + { + uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]); + expand_nmax = -1; /* reset */ - /* Must not do this check until after we have saved the result of the - regex match above. */ + /* Must not do this check until after we have saved the result of the + regex match above. */ - if (require_auth == OK || - verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name, - host->address, NULL) == OK) - { - auth_instance *au; - fail_reason = US"no common mechanisms were found"; + if (require_auth == OK || + verify_check_given_host(&ob->hosts_try_auth, host) == OK) + { + auth_instance *au; + fail_reason = US"no common mechanisms were found"; - DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n"); + DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n"); - /* Scan the configured authenticators looking for one which is configured - for use as a client, which is not suppressed by client_condition, and - whose name matches an authentication mechanism supported by the server. - If one is found, attempt to authenticate by calling its client function. - */ + /* Scan the configured authenticators looking for one which is configured + for use as a client, which is not suppressed by client_condition, and + whose name matches an authentication mechanism supported by the server. + If one is found, attempt to authenticate by calling its client function. + */ - for (au = auths; !smtp_authenticated && au != NULL; au = au->next) - { - uschar *p = names; - if (!au->client || - (au->client_condition != NULL && - !expand_check_condition(au->client_condition, au->name, - US"client authenticator"))) - { - DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n", - au->name, - (au->client)? "client_condition is false" : - "not configured as a client"); - continue; - } + for (au = auths; !smtp_authenticated && au != NULL; au = au->next) + { + uschar *p = names; + if (!au->client || + (au->client_condition != NULL && + !expand_check_condition(au->client_condition, au->name, + US"client authenticator"))) + { + DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n", + au->name, + (au->client)? "client_condition is false" : + "not configured as a client"); + continue; + } - /* Loop to scan supported server mechanisms */ + /* Loop to scan supported server mechanisms */ - while (*p != 0) - { - int rc; - int len = Ustrlen(au->public_name); - while (isspace(*p)) p++; + while (*p != 0) + { + int rc; + int len = Ustrlen(au->public_name); + while (isspace(*p)) p++; - if (strncmpic(au->public_name, p, len) != 0 || - (p[len] != 0 && !isspace(p[len]))) - { - while (*p != 0 && !isspace(*p)) p++; - continue; - } + if (strncmpic(au->public_name, p, len) != 0 || + (p[len] != 0 && !isspace(p[len]))) + { + while (*p != 0 && !isspace(*p)) p++; + continue; + } - /* Found data for a listed mechanism. Call its client entry. Set - a flag in the outblock so that data is overwritten after sending so - that reflections don't show it. */ - - fail_reason = US"authentication attempt(s) failed"; - obp->authenticating = TRUE; - rc = (au->info->clientcode)(au, ibp, obp, - ob->command_timeout, buffer, bufsize); - obp->authenticating = FALSE; - DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", - au->name, rc); - - /* A temporary authentication failure must hold up delivery to - this host. After a permanent authentication failure, we carry on - to try other authentication methods. If all fail hard, try to - deliver the message unauthenticated unless require_auth was set. */ + /* Found data for a listed mechanism. Call its client entry. Set + a flag in the outblock so that data is overwritten after sending so + that reflections don't show it. */ + + fail_reason = US"authentication attempt(s) failed"; + obp->authenticating = TRUE; + rc = (au->info->clientcode)(au, ibp, obp, + ob->command_timeout, buffer, bufsize); + obp->authenticating = FALSE; + DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", + au->name, rc); + + /* A temporary authentication failure must hold up delivery to + this host. After a permanent authentication failure, we carry on + to try other authentication methods. If all fail hard, try to + deliver the message unauthenticated unless require_auth was set. */ - switch(rc) - { - case OK: - smtp_authenticated = TRUE; /* stops the outer loop */ - client_authenticator = au->name; - if (au->set_client_id != NULL) - client_authenticated_id = expand_string(au->set_client_id); - break; - - /* Failure after writing a command */ - - case FAIL_SEND: - return FAIL_SEND; - - /* Failure after reading a response */ - - case FAIL: - if (errno != 0 || buffer[0] != '5') return FAIL; - log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s", - au->name, host->name, host->address, buffer); - break; - - /* Failure by some other means. In effect, the authenticator - decided it wasn't prepared to handle this case. Typically this - is the result of "fail" in an expansion string. Do we need to - log anything here? Feb 2006: a message is now put in the buffer - if logging is required. */ - - case CANCELLED: - if (*buffer != 0) - log_write(0, LOG_MAIN, "%s authenticator cancelled " - "authentication H=%s [%s] %s", au->name, host->name, - host->address, buffer); - break; - - /* Internal problem, message in buffer. */ - - case ERROR: - set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE); - return ERROR; - } + switch(rc) + { + case OK: + smtp_authenticated = TRUE; /* stops the outer loop */ + client_authenticator = au->name; + if (au->set_client_id != NULL) + client_authenticated_id = expand_string(au->set_client_id); + break; + + /* Failure after writing a command */ + + case FAIL_SEND: + return FAIL_SEND; + + /* Failure after reading a response */ + + case FAIL: + if (errno != 0 || buffer[0] != '5') return FAIL; + log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s", + au->name, host->name, host->address, buffer); + break; + + /* Failure by some other means. In effect, the authenticator + decided it wasn't prepared to handle this case. Typically this + is the result of "fail" in an expansion string. Do we need to + log anything here? Feb 2006: a message is now put in the buffer + if logging is required. */ + + case CANCELLED: + if (*buffer != 0) + log_write(0, LOG_MAIN, "%s authenticator cancelled " + "authentication H=%s [%s] %s", au->name, host->name, + host->address, buffer); + break; + + /* Internal problem, message in buffer. */ + + case ERROR: + set_errno(addrlist, ERRNO_AUTHPROB, string_copy(buffer), + DEFER, FALSE, NULL); + return ERROR; + } - break; /* If not authenticated, try next authenticator */ - } /* Loop for scanning supported server mechanisms */ - } /* Loop for further authenticators */ - } + break; /* If not authenticated, try next authenticator */ + } /* Loop for scanning supported server mechanisms */ + } /* Loop for further authenticators */ } + } - /* If we haven't authenticated, but are required to, give up. */ +/* If we haven't authenticated, but are required to, give up. */ - if (require_auth == OK && !smtp_authenticated) - { - set_errno(addrlist, ERRNO_AUTHFAIL, - string_sprintf("authentication required but %s", fail_reason), DEFER, - FALSE); - return DEFER; - } +if (require_auth == OK && !smtp_authenticated) + { + set_errno(addrlist, ERRNO_AUTHFAIL, + string_sprintf("authentication required but %s", fail_reason), DEFER, + FALSE, NULL); + return DEFER; + } - return OK; +return OK; } @@ -1115,7 +1152,7 @@ { uschar *message = string_sprintf("failed to expand " "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); + set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); return TRUE; } } @@ -1140,6 +1177,117 @@ +#ifdef EXPERIMENTAL_DANE +int +tlsa_lookup(const host_item * host, dns_answer * dnsa, + BOOL dane_required, BOOL * dane) +{ +/* move this out to host.c given the similarity to dns_lookup() ? */ +uschar buffer[300]; +const uschar * fullname = buffer; + +/* TLSA lookup string */ +(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name); + +switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname)) + { + case DNS_AGAIN: + return DEFER; /* just defer this TLS'd conn */ + + default: + case DNS_FAIL: + if (dane_required) + return FAIL; + break; + + case DNS_SUCCEED: + if (!dns_is_secure(dnsa)) + { + log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC"); + return DEFER; + } + *dane = TRUE; + break; + } +return OK; +} +#endif + + + +typedef struct smtp_compare_s +{ + uschar *current_sender_address; + struct transport_instance *tblock; +} smtp_compare_t; + +/* +Create a unique string that identifies this message, it is based on +sender_address, helo_data and tls_certificate if enabled. */ + +static uschar * +smtp_local_identity(uschar * sender, struct transport_instance * tblock) +{ +address_item * addr1; +uschar * if1 = US""; +uschar * helo1 = US""; +#ifdef SUPPORT_TLS +uschar * tlsc1 = US""; +#endif +uschar * save_sender_address = sender_address; +uschar * local_identity = NULL; +smtp_transport_options_block * ob = + (smtp_transport_options_block *)tblock->options_block; + +sender_address = sender; + +addr1 = deliver_make_addr (sender, TRUE); +deliver_set_expansions(addr1); + +if (ob->interface) + if1 = expand_string(ob->interface); + +if (ob->helo_data) + helo1 = expand_string(ob->helo_data); + +#ifdef SUPPORT_TLS +if (ob->tls_certificate) + tlsc1 = expand_string(ob->tls_certificate); +local_identity = string_sprintf ("%s^%s^%s", if1, helo1, tlsc1); +#else +local_identity = string_sprintf ("%s^%s", if1, helo1); +#endif + +deliver_set_expansions(NULL); +sender_address = save_sender_address; + +return local_identity; +} + + + +/* This routine is a callback that is called from transport_check_waiting. +This function will evaluate the incoming message versus the previous +message. If the incoming message is using a different local identity then +we will veto this new message. */ + +static BOOL +smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare) +{ +uschar * save_sender_address = sender_address; +uschar * current_local_identity = + smtp_local_identity(s_compare->current_sender_address, s_compare->tblock); +uschar * new_sender_address = deliver_get_sender_address(message_id); +uschar * message_local_identity = + smtp_local_identity(new_sender_address, s_compare->tblock); + +sender_address = save_sender_address; + +return Ustrcmp(current_local_identity, message_local_identity) == 0; +} + + + /************************************************* * Deliver address list to given host * *************************************************/ @@ -1166,8 +1314,6 @@ port default TCP/IP port to use, in host byte order interface interface to bind to, or NULL tblock transport instance block - copy_host TRUE if host set in addr->host_used must be copied, because - it is specific to this call of the transport message_defer set TRUE if yield is OK, but all addresses were deferred because of a non-recipient, non-host failure, that is, a 4xx response to MAIL FROM, DATA, or ".". This is a defer @@ -1188,7 +1334,7 @@ static int smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port, - uschar *interface, transport_instance *tblock, BOOL copy_host, + uschar *interface, transport_instance *tblock, BOOL *message_defer, BOOL suppress_tls) { address_item *addr; @@ -1215,14 +1361,23 @@ BOOL prdr_offered = FALSE; BOOL prdr_active; #endif -#ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_INTERNATIONAL +BOOL utf8_needed = FALSE; +BOOL utf8_offered = FALSE; +#endif BOOL dsn_all_lasthop = TRUE; +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) +BOOL dane = FALSE; +BOOL dane_required = verify_check_given_host(&ob->hosts_require_dane, host) == OK; +dns_answer tlsa_dnsa; #endif smtp_inblock inblock; smtp_outblock outblock; int max_rcpt = tblock->max_addresses; uschar *igquotstr = US""; + uschar *helo_data = NULL; + uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; uschar *p; @@ -1272,7 +1427,8 @@ #ifndef SUPPORT_TLS if (smtps) { - set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE); + set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", + DEFER, FALSE, NULL); return ERROR; } #endif @@ -1283,22 +1439,69 @@ if (continue_hostname == NULL) { + /* This puts port into host->port */ inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, ob->connect_timeout, - ob->keepalive, ob->dscp); /* This puts port into host->port */ + smtp_connect(host, host_af, port, interface, ob->connect_timeout, tblock); if (inblock.sock < 0) { set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, - NULL, DEFER, FALSE); + NULL, DEFER, FALSE, NULL); return DEFER; } +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { + tls_out.dane_verified = FALSE; + tls_out.tlsa_usage = 0; + + if (host->dnssec == DS_YES) + { + if( ( dane_required + || verify_check_given_host(&ob->hosts_try_dane, host) == OK + ) + && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK + && dane_required /* do not error on only dane-requested */ + ) + { + set_errno(addrlist, ERRNO_DNSDEFER, + string_sprintf("DANE error: tlsa lookup %s", + rc == DEFER ? "DEFER" : "FAIL"), + rc, FALSE, NULL); + return rc; + } + } + else if (dane_required) + { + set_errno(addrlist, ERRNO_DNSDEFER, + string_sprintf("DANE error: %s lookup not DNSSEC", host->name), + FAIL, FALSE, NULL); + return FAIL; + } + + if (dane) + ob->tls_tempfail_tryclear = FALSE; + } +#endif /*DANE*/ + /* Expand the greeting message while waiting for the initial response. (Makes sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is delayed till here so that $sending_interface and $sending_port are set. */ helo_data = expand_string(ob->helo_data); +#ifdef EXPERIMENTAL_INTERNATIONAL + if (helo_data) + { + uschar * errstr = NULL; + if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr) + { + errstr = string_sprintf("failed to expand helo_data: %s", errstr); + set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL); + yield = DEFER; + goto SEND_QUIT; + } + } +#endif /* The first thing is to wait for an initial OK response. The dreaded "goto" is nevertheless a reasonably clean way of programming this kind of logic, @@ -1309,6 +1512,23 @@ if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) goto RESPONSE_FAILED; +#ifdef EXPERIMENTAL_EVENT + { + uschar * s; + lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes" + : host->dnssec==DS_NO ? US"no" : NULL; + s = event_raise(tblock->event_action, US"smtp:connect", buffer); + if (s) + { + set_errno(addrlist, ERRNO_EXPANDFAIL, + string_sprintf("deferred by smtp:connect event expansion: %s", s), + DEFER, FALSE, NULL); + yield = DEFER; + goto SEND_QUIT; + } + } +#endif + /* Now check if the helo_data expansion went well, and sign off cleanly if it didn't. */ @@ -1316,7 +1536,7 @@ { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); + set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); yield = DEFER; goto SEND_QUIT; } @@ -1357,13 +1577,12 @@ mailers use upper case for some reason (the RFC is quite clear about case independence) so, for peace of mind, I gave in. */ - esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL, - host->name, host->address, NULL) != OK; + esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK; /* Alas; be careful, since this goto is not an error-out, so conceivably we might set data between here and the target which we assume to exist and be usable. I can see this coming back to bite us. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (smtps) { tls_offered = TRUE; @@ -1372,7 +1591,7 @@ smtp_command = US"SSL-on-connect"; goto TLS_NEGOTIATE; } - #endif +#endif if (esmtp) { @@ -1409,22 +1628,35 @@ /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_offered = esmtp && pcre_exec(regex_STARTTLS, NULL, CS buffer, Ustrlen(buffer), 0, PCRE_EOPT, NULL, 0) >= 0; - #endif +#endif - #ifndef DISABLE_PRDR - prdr_offered = esmtp && - (pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0, - PCRE_EOPT, NULL, 0) >= 0) && - (verify_check_this_host(&(ob->hosts_try_prdr), NULL, host->name, - host->address, NULL) == OK); +#ifndef DISABLE_PRDR + prdr_offered = esmtp + && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0, + PCRE_EOPT, NULL, 0) >= 0 + && verify_check_given_host(&ob->hosts_try_prdr, host) == OK; if (prdr_offered) {DEBUG(D_transport) debug_printf("PRDR usable\n");} - #endif +#endif + +#ifdef EXPERIMENTAL_INTERNATIONAL + if (addrlist->prop.utf8_msg) + { + utf8_needed = !addrlist->prop.utf8_downcvt + && !addrlist->prop.utf8_downcvt_maybe; + DEBUG(D_transport) if (!utf8_needed) debug_printf("utf8: %s downconvert\n", + addrlist->prop.utf8_downcvt ? "mandatory" : "optional"); + + utf8_offered = esmtp + && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; + } +#endif } /* For continuing deliveries down the same channel, the socket is the standard @@ -1450,9 +1682,9 @@ for error analysis. */ #ifdef SUPPORT_TLS -if (tls_offered && !suppress_tls && - verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name, - host->address, NULL) != OK) +if ( tls_offered + && !suppress_tls + && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK) { uschar buffer2[4096]; if (smtp_write_command(&outblock, FALSE, "STARTTLS\r\n") < 0) @@ -1481,7 +1713,11 @@ else TLS_NEGOTIATE: { - int rc = tls_client_start(inblock.sock, host, addrlist, ob); + int rc = tls_client_start(inblock.sock, host, addrlist, tblock +# ifdef EXPERIMENTAL_DANE + , dane ? &tlsa_dnsa : NULL +# endif + ); /* TLS negotiation failed; give an error. From outside, this function may be called again to try in clear on a new connection, if the options permit @@ -1489,6 +1725,17 @@ if (rc != OK) { +# ifdef EXPERIMENTAL_DANE + if (rc == DEFER && dane && !dane_required) + { + log_write(0, LOG_MAIN, "DANE attempt failed;" + " trying CA-root TLS to %s [%s] (not in hosts_require_dane)", + host->name, host->address); + dane = FALSE; + goto TLS_NEGOTIATE; + } +# endif + save_errno = ERRNO_TLSFAILURE; message = US"failure while setting up TLS session"; send_quit = FALSE; @@ -1498,7 +1745,6 @@ /* TLS session is set up */ for (addr = addrlist; addr != NULL; addr = addr->next) - { if (addr->transport_return == PENDING_DEFER) { addr->cipher = tls_out.cipher; @@ -1507,7 +1753,6 @@ addr->peerdn = tls_out.peerdn; addr->ocsp = tls_out.ocsp; } - } } } @@ -1531,7 +1776,7 @@ { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); + set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); yield = DEFER; goto SEND_QUIT; } @@ -1564,17 +1809,20 @@ /* If the host is required to use a secure channel, ensure that we have one. */ -else if (verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) == OK) +else if ( +# ifdef EXPERIMENTAL_DANE + dane || +# endif + verify_check_given_host(&ob->hosts_require_tls, host) == OK + ) { save_errno = ERRNO_TLSREQUIRED; - message = string_sprintf("a TLS session is required for %s [%s], but %s", - host->name, host->address, + message = string_sprintf("a TLS session is required, but %s", tls_offered? "an attempt to start TLS failed" : "the server did not offer TLS support"); goto TLS_FAILED; } -#endif +#endif /*SUPPORT_TLS*/ /* If TLS is active, we have just started it up and re-done the EHLO command, so its response needs to be analyzed. If TLS is not active and this is a @@ -1582,9 +1830,9 @@ we skip this. */ if (continue_hostname == NULL - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS || tls_out.active >= 0 - #endif +#endif ) { /* Set for IGNOREQUOTA if the response to LHLO specifies support and the @@ -1605,32 +1853,36 @@ the current host, esmtp will be false, so PIPELINING can never be used. If the current host matches hosts_avoid_pipelining, don't do it. */ - smtp_use_pipelining = esmtp && - verify_check_this_host(&(ob->hosts_avoid_pipelining), NULL, host->name, - host->address, NULL) != OK && - pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0, - PCRE_EOPT, NULL, 0) >= 0; + smtp_use_pipelining = esmtp + && verify_check_given_host(&ob->hosts_avoid_pipelining, host) != OK + && pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; DEBUG(D_transport) debug_printf("%susing PIPELINING\n", smtp_use_pipelining? "" : "not "); #ifndef DISABLE_PRDR - prdr_offered = esmtp && - pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0, - PCRE_EOPT, NULL, 0) >= 0 && - verify_check_this_host(&(ob->hosts_try_prdr), NULL, host->name, - host->address, NULL) == OK; + prdr_offered = esmtp + && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0 + && verify_check_given_host(&ob->hosts_try_prdr, host) == OK; if (prdr_offered) {DEBUG(D_transport) debug_printf("PRDR usable\n");} #endif -#ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_INTERNATIONAL + if (addrlist->prop.utf8_msg) + utf8_offered = esmtp + && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; +#endif + /* Note if the server supports DSN */ - smtp_use_dsn = esmtp && pcre_exec(regex_DSN, NULL, CS buffer, (int)Ustrlen(CS buffer), 0, - PCRE_EOPT, NULL, 0) >= 0; + smtp_use_dsn = esmtp + && pcre_exec(regex_DSN, NULL, CS buffer, Ustrlen(CS buffer), 0, + PCRE_EOPT, NULL, 0) >= 0; DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn); -#endif /* Note if the response to EHLO specifies support for the AUTH extension. If it has, check that this host is one we want to authenticate to, and do @@ -1652,6 +1904,15 @@ setting_up = FALSE; +#ifdef EXPERIMENTAL_INTERNATIONAL +/* If this is an international message we need the host to speak SMTPUTF8 */ +if (utf8_needed && !utf8_offered) + { + errno = ERRNO_UTF8_FWD; + goto RESPONSE_FAILED; + } +#endif + /* If there is a filter command specified for this transport, we can now set it up. This cannot be done until the identify of the host is known. */ @@ -1670,7 +1931,7 @@ if (!rc) { set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, - FALSE); + FALSE, NULL); yield = ERROR; goto SEND_QUIT; } @@ -1721,36 +1982,41 @@ { /* at least two recipients to send */ prdr_active = TRUE; sprintf(CS p, " PRDR"); p += 5; - goto prdr_is_active; + break; } break; } } -prdr_is_active: #endif -#ifdef EXPERIMENTAL_DSN +#ifdef EXPERIMENTAL_INTERNATIONAL +if (addrlist->prop.utf8_msg && !addrlist->prop.utf8_downcvt && utf8_offered) + sprintf(CS p, " SMTPUTF8"), p += 9; +#endif + /* check if all addresses have lasthop flag */ /* do not send RET and ENVID if true */ -dsn_all_lasthop = TRUE; -for (addr = first_addr; +for (dsn_all_lasthop = TRUE, addr = first_addr; address_count < max_rcpt && addr != NULL; addr = addr->next) if ((addr->dsn_flags & rf_dsnlasthop) != 1) + { dsn_all_lasthop = FALSE; + break; + } /* Add any DSN flags to the mail command */ -if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE)) +if (smtp_use_dsn && !dsn_all_lasthop) { if (dsn_ret == dsn_ret_hdrs) { - strcpy(p, " RET=HDRS"); + Ustrcpy(p, " RET=HDRS"); while (*p) p++; } else if (dsn_ret == dsn_ret_full) { - strcpy(p, " RET=FULL"); + Ustrcpy(p, " RET=FULL"); while (*p) p++; } if (dsn_envid != NULL) @@ -1759,7 +2025,6 @@ while (*p) p++; } } -#endif /* If an authenticated_sender override has been specified for this transport instance, expand it. If the expansion is forced to fail, and there was already @@ -1769,7 +2034,10 @@ cases where non-standard addresses (e.g. without domains) might be required. */ if (smtp_mail_auth_str(p, sizeof(buffer) - (p-buffer), addrlist, ob)) - return ERROR; + { + yield = ERROR; + goto SEND_QUIT; + } /* From here until we send the DATA command, we can make use of PIPELINING if the server host supports it. The code has to be able to check the responses @@ -1779,28 +2047,51 @@ pending_MAIL = TRUE; /* The block starts with MAIL */ -rc = smtp_write_command(&outblock, smtp_use_pipelining, - "MAIL FROM:<%s>%s\r\n", return_path, buffer); + { + uschar * s = return_path; +#ifdef EXPERIMENTAL_INTERNATIONAL + uschar * errstr = NULL; + + /* If we must downconvert, do the from-address here. Remember we had to + for the to-addresses (done below), and also (ugly) for re-doing when building + the delivery log line. */ + + if (addrlist->prop.utf8_msg && (addrlist->prop.utf8_downcvt || !utf8_offered)) + { + if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr) + { + set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL); + yield = ERROR; + goto SEND_QUIT; + } + setflag(addrlist, af_utf8_downcvt); + } +#endif + + rc = smtp_write_command(&outblock, smtp_use_pipelining, + "MAIL FROM:<%s>%s\r\n", s, buffer); + } + mail_command = string_copy(big_buffer); /* Save for later error message */ switch(rc) { case -1: /* Transmission error */ - goto SEND_FAILED; + goto SEND_FAILED; case +1: /* Block was sent */ - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) - { - if (errno == 0 && buffer[0] == '4') { - errno = ERRNO_MAIL4XX; - addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + if (errno == 0 && buffer[0] == '4') + { + errno = ERRNO_MAIL4XX; + addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8; + } + goto RESPONSE_FAILED; } - goto RESPONSE_FAILED; - } - pending_MAIL = FALSE; - break; + pending_MAIL = FALSE; + break; } /* Pass over all the relevant recipient addresses for this host, which are the @@ -1822,66 +2113,69 @@ { int count; BOOL no_flush; + uschar * rcpt_addr; - #ifdef EXPERIMENTAL_DSN - if(smtp_use_dsn) - addr->dsn_aware = dsn_support_yes; - else - addr->dsn_aware = dsn_support_no; - #endif + addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no; if (addr->transport_return != PENDING_DEFER) continue; address_count++; no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL); - #ifdef EXPERIMENTAL_DSN /* Add any DSN flags to the rcpt command and add to the sent string */ p = buffer; *p = 0; - if ((smtp_use_dsn) && ((addr->dsn_flags & rf_dsnlasthop) != 1)) + if (smtp_use_dsn && (addr->dsn_flags & rf_dsnlasthop) != 1) { if ((addr->dsn_flags & rf_dsnflags) != 0) { int i; BOOL first = TRUE; - strcpy(p, " NOTIFY="); + Ustrcpy(p, " NOTIFY="); while (*p) p++; for (i = 0; i < 4; i++) - { if ((addr->dsn_flags & rf_list[i]) != 0) { if (!first) *p++ = ','; first = FALSE; - strcpy(p, rf_names[i]); + Ustrcpy(p, rf_names[i]); while (*p) p++; } - } } - if (addr->dsn_orcpt != NULL) { + if (addr->dsn_orcpt != NULL) + { string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s", addr->dsn_orcpt); while (*p) p++; } } - #endif - /* Now send the RCPT command, and process outstanding responses when necessary. After a timeout on RCPT, we just end the function, leaving the yield as OK, because this error can often mean that there is a problem with just one address, so we don't want to delay the host. */ - #ifdef EXPERIMENTAL_DSN + rcpt_addr = transport_rcpt_address(addr, tblock->rcpt_include_affixes); + +#ifdef EXPERIMENTAL_INTERNATIONAL + { + uschar * dummy_errstr; + if ( testflag(addrlist, af_utf8_downcvt) + && (rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, &dummy_errstr), + dummy_errstr + ) ) + { + errno = ERRNO_EXPANDFAIL; + goto SEND_FAILED; + } + } +#endif + count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", - transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer); - #else - count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n", - transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr); - #endif + rcpt_addr, igquotstr, buffer); if (count < 0) goto SEND_FAILED; if (count > 0) @@ -1914,16 +2208,15 @@ if (mua_wrapper) { address_item *badaddr; - for (badaddr = first_addr; badaddr != NULL; badaddr = badaddr->next) - { - if (badaddr->transport_return != PENDING_OK) break; - } - if (badaddr != NULL) - { - set_errno(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message)); - ok = FALSE; - } + for (badaddr = first_addr; badaddr; badaddr = badaddr->next) + if (badaddr->transport_return != PENDING_OK) + { + /*XXX could we find a better errno than 0 here? */ + set_errno(addrlist, 0, badaddr->message, FAIL, + testflag(badaddr, af_pass_message), NULL); + ok = FALSE; + break; + } } /* If ok is TRUE, we know we have got at least one good recipient, and must now @@ -1972,6 +2265,7 @@ DEBUG(D_transport|D_v) debug_printf(" SMTP>> writing message and terminating \".\"\n"); transport_count = 0; + #ifndef DISABLE_DKIM ok = dkim_transport_write_message(addrlist, inblock.sock, topt_use_crlf | topt_end_dot | topt_escape_headers | @@ -2079,33 +2373,21 @@ int flag = '='; int delivery_time = (int)(time(NULL) - start_delivery_time); int len; - host_item *thost; uschar *conf = NULL; send_rset = FALSE; - /* Make a copy of the host if it is local to this invocation - of the transport. */ - - if (copy_host) - { - thost = store_get(sizeof(host_item)); - *thost = *host; - thost->name = string_copy(host->name); - thost->address = string_copy(host->address); - } - else thost = host; - /* Set up confirmation if needed - applies only to SMTP */ if ( - #ifndef EXPERIMENTAL_TPDA +#ifndef EXPERIMENTAL_EVENT (log_extra_selector & LX_smtp_confirmation) != 0 && - #endif +#endif !lmtp ) { - uschar *s = string_printing(buffer); - conf = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ + conf = (s == buffer)? (uschar *)string_copy(s) : US s; } /* Process all transported addresses - for LMTP or PRDR, read a status for @@ -2155,8 +2437,9 @@ completed_address = TRUE; /* NOW we can set this flag */ if ((log_extra_selector & LX_smtp_confirmation) != 0) { - uschar *s = string_printing(buffer); - conf = (s == buffer)? (uschar *)string_copy(s) : s; + const uschar *s = string_printing(buffer); + /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ + conf = (s == buffer)? (uschar *)string_copy(s) : US s; } } @@ -2165,7 +2448,7 @@ addr->transport_return = OK; addr->more_errno = delivery_time; - addr->host_used = thost; + addr->host_used = host; addr->special_action = flag; addr->message = conf; #ifndef DISABLE_PRDR @@ -2276,10 +2559,10 @@ in message and save_errno, and setting_up will always be true. Treat as a temporary error. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS TLS_FAILED: code = '4'; - #endif +#endif /* If the failure happened while setting up the call, see if the failure was a 5xx response (this will either be on connection, or following HELO - a 5xx @@ -2294,12 +2577,10 @@ if (setting_up) { if (code == '5') - { - set_errno(addrlist, save_errno, message, FAIL, pass_message); - } + set_errno(addrlist, save_errno, message, FAIL, pass_message, host); else { - set_errno(addrlist, save_errno, message, DEFER, pass_message); + set_errno(addrlist, save_errno, message, DEFER, pass_message, host); yield = DEFER; } } @@ -2316,24 +2597,29 @@ switch(save_errno) { +#ifdef EXPERIMENTAL_INTERNATIONAL + case ERRNO_UTF8_FWD: + code = '5'; + /*FALLTHROUGH*/ +#endif case 0: case ERRNO_MAIL4XX: case ERRNO_DATA4XX: - message_error = TRUE; - break; + message_error = TRUE; + break; case ETIMEDOUT: - message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 || - Ustrncmp(smtp_command,"end ",4) == 0; - break; + message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 || + Ustrncmp(smtp_command,"end ",4) == 0; + break; case ERRNO_SMTPCLOSED: - message_error = Ustrncmp(smtp_command,"end ",4) == 0; - break; + message_error = Ustrncmp(smtp_command,"end ",4) == 0; + break; default: - message_error = FALSE; - break; + message_error = FALSE; + break; } /* Handle the cases that are treated as message errors. These are: @@ -2341,6 +2627,7 @@ (a) negative response or timeout after MAIL (b) negative response after DATA (c) negative response or timeout or dropped connection after "." + (d) utf8 support required and not offered It won't be a negative response or timeout after RCPT, as that is dealt with separately above. The action in all cases is to set an appropriate @@ -2355,7 +2642,7 @@ { if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */ set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER, - pass_message); + pass_message, host); /* If there's an errno, the message contains just the identity of the host. */ @@ -2365,7 +2652,7 @@ if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message); - deliver_msglog("%s %s\n", tod_stamp(tod_log), message); + msglog_line(host, message); *message_defer = TRUE; } } @@ -2380,7 +2667,7 @@ { yield = (save_errno == ERRNO_CHHEADER_FAIL || save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER; - set_errno(addrlist, save_errno, message, DEFER, pass_message); + set_errno(addrlist, save_errno, message, DEFER, pass_message, host); } } } @@ -2422,15 +2709,21 @@ if (completed_address && ok && send_quit) { BOOL more; - if (first_addr != NULL || continue_more || - ( - (tls_out.active < 0 || - verify_check_this_host(&(ob->hosts_nopass_tls), NULL, host->name, - host->address, NULL) != OK) + smtp_compare_t t_compare; + + t_compare.tblock = tblock; + t_compare.current_sender_address = sender_address; + + if ( first_addr != NULL + || continue_more + || ( ( tls_out.active < 0 + || verify_check_given_host(&ob->hosts_nopass_tls, host) != OK + ) && transport_check_waiting(tblock->name, host->name, - tblock->connection_max_messages, new_message_id, &more) - )) + tblock->connection_max_messages, new_message_id, &more, + (oicf)smtp_are_same_identities, (void*)&t_compare) + ) ) { uschar *msg; BOOL pass_message; @@ -2451,7 +2744,8 @@ &pass_message); if (!send_quit) { - DEBUG(D_transport) debug_printf("%s\n", msg); + DEBUG(D_transport) debug_printf("H=%s [%s] %s\n", + host->name, host->address, msg); } } } @@ -2472,7 +2766,7 @@ when TLS is shut down. We test for this by sending a new EHLO. If we don't get a good response, we don't attempt to pass the socket on. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (tls_out.active >= 0) { tls_close(FALSE, TRUE); @@ -2483,7 +2777,7 @@ smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout); } - #endif +#endif /* If the socket is successfully passed, we musn't send QUIT (or indeed anything!) from here. */ @@ -2497,7 +2791,7 @@ /* If RSET failed and there are addresses left, they get deferred. */ - else set_errno(first_addr, errno, msg, DEFER, FALSE); + else set_errno(first_addr, errno, msg, DEFER, FALSE, host); } } @@ -2539,6 +2833,11 @@ case continue_more won't get set. */ (void)close(inblock.sock); + +#ifdef EXPERIMENTAL_EVENT +(void) event_raise(tblock->event_action, US"tcp:close", NULL); +#endif + continue_transport = NULL; continue_hostname = NULL; return yield; @@ -2620,21 +2919,21 @@ address_item *first_addr = NULL; address_item *addr; for (addr = addrlist; addr != NULL; addr = addr->next) - { - if (addr->transport_return != DEFER) continue; - if (first_addr == NULL) first_addr = addr; - addr->transport_return = PENDING_DEFER; - addr->basic_errno = 0; - addr->more_errno = (host->mx >= 0)? 'M' : 'A'; - addr->message = NULL; - #ifdef SUPPORT_TLS - addr->cipher = NULL; - addr->ourcert = NULL; - addr->peercert = NULL; - addr->peerdn = NULL; - addr->ocsp = OCSP_NOT_REQ; - #endif - } + if (addr->transport_return == DEFER) + { + if (first_addr == NULL) first_addr = addr; + addr->transport_return = PENDING_DEFER; + addr->basic_errno = 0; + addr->more_errno = (host->mx >= 0)? 'M' : 'A'; + addr->message = NULL; +#ifdef SUPPORT_TLS + addr->cipher = NULL; + addr->ourcert = NULL; + addr->peercert = NULL; + addr->peerdn = NULL; + addr->ocsp = OCSP_NOT_REQ; +#endif + } return first_addr; } @@ -2723,8 +3022,7 @@ if (Ustrchr(s, '$') != NULL) { - expanded_hosts = expand_string(s); - if (expanded_hosts == NULL) + if (!(expanded_hosts = expand_string(s))) { addrlist->message = string_sprintf("failed to expand list of hosts " "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message); @@ -2752,13 +3050,14 @@ /* If there was no expansion of hosts, save the host list for next time. */ - if (expanded_hosts == NULL) ob->hostlist = hostlist; + if (!expanded_hosts) ob->hostlist = hostlist; } /* This is not the first time this transport has been run in this delivery; the host list was built previously. */ - else hostlist = ob->hostlist; + else + hostlist = ob->hostlist; } /* The host list was supplied with the address. If hosts_randomize is set, we @@ -2802,12 +3101,10 @@ hostlist = addrlist->host_list = newlist; } - /* Sort out the default port. */ if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE; - /* For each host-plus-IP-address on the list: . If this is a continued delivery and the host isn't the one with the @@ -2904,7 +3201,6 @@ { int new_port, flags; host_item *hh; - uschar *canonical_name; if (host->status >= hstatus_unusable) { @@ -2932,11 +3228,11 @@ if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS; if (ob->gethostbyname || string_is_ip_address(host->name, NULL) != 0) - rc = host_find_byname(host, NULL, flags, &canonical_name, TRUE); + rc = host_find_byname(host, NULL, flags, NULL, TRUE); else rc = host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - ob->dnssec_request_domains, ob->dnssec_require_domains, - &canonical_name, NULL); + &ob->dnssec, /* domains for request/require */ + NULL, NULL); /* Update the host (and any additional blocks, resulting from multihoming) with a host-specific port, if any. */ @@ -3012,7 +3308,8 @@ doing a two-stage queue run, don't do this if forcing. */ if ((!deliver_force || queue_2stage) && (queue_smtp || - match_isinlist(addrlist->domain, &queue_smtp_domains, 0, + match_isinlist(addrlist->domain, + (const uschar **)&queue_smtp_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)) { expired = FALSE; @@ -3064,14 +3361,20 @@ if (cutoff_retry == 0) { + BOOL incl_ip; /* Ensure the status of the address is set by checking retry data if - necessary. There maybe host-specific retry data (applicable to all + necessary. There may be host-specific retry data (applicable to all messages) and also data for retries of a specific message at this host. If either of these retry records are actually read, the keys used are returned to save recomputing them later. */ + if (exp_bool(addrlist, US"transport", tblock->name, D_transport, + US"retry_include_ip_address", ob->retry_include_ip_address, + ob->expand_retry_include_ip_address, &incl_ip) != OK) + continue; /* with next host */ + host_is_expired = retry_check_address(addrlist->domain, host, pistring, - ob->retry_include_ip_address, &retry_host_key, &retry_message_key); + incl_ip, &retry_host_key, &retry_message_key); DEBUG(D_transport) debug_printf("%s [%s]%s status = %s\n", host->name, (host->address == NULL)? US"" : host->address, pistring, @@ -3134,8 +3437,7 @@ sending the message down a pre-existing connection. */ if (!continuing && - verify_check_this_host(&(ob->serialize_hosts), NULL, host->name, - host->address, NULL) == OK) + verify_check_given_host(&ob->serialize_hosts, host) == OK) { serialize_key = string_sprintf("host-serialize-%s", host->name); if (!enq_start(serialize_key)) @@ -3172,7 +3474,7 @@ if (dont_deliver) { host_item *host2; - set_errno(addrlist, 0, NULL, OK, FALSE); + set_errno(addrlist, 0, NULL, OK, FALSE, NULL); for (addr = addrlist; addr != NULL; addr = addr->next) { addr->host_used = host; @@ -3205,6 +3507,20 @@ else { + host_item * thost; + /* Make a copy of the host if it is local to this invocation + of the transport. */ + + if (expanded_hosts) + { + thost = store_get(sizeof(host_item)); + *thost = *host; + thost->name = string_copy(host->name); + thost->address = string_copy(host->address); + } + else + thost = host; + if (!host_is_expired && ++unexpired_hosts_tried >= ob->hosts_max_try) { host_item *h; @@ -3224,8 +3540,8 @@ /* Attempt the delivery. */ total_hosts_tried++; - rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock, - expanded_hosts != NULL, &message_defer, FALSE); + rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + &message_defer, FALSE); /* Yield is one of: OK => connection made, each address contains its result; @@ -3246,10 +3562,10 @@ first_addr->basic_errno != ERRNO_TLSFAILURE) write_logs(first_addr, host); - #ifdef EXPERIMENTAL_TPDA +#ifdef EXPERIMENTAL_EVENT if (rc == DEFER) - tpda_deferred(ob, first_addr, host); - #endif + deferred_event_raise(first_addr, host); +#endif /* If STARTTLS was accepted, but there was a failure in setting up the TLS session (usually a certificate screwup), and the host is not in @@ -3260,25 +3576,26 @@ session, so the in-clear transmission after those errors, if permitted, happens inside smtp_deliver().] */ - #ifdef SUPPORT_TLS - if (rc == DEFER && first_addr->basic_errno == ERRNO_TLSFAILURE && - ob->tls_tempfail_tryclear && - verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) != OK) +#ifdef SUPPORT_TLS + if ( rc == DEFER + && first_addr->basic_errno == ERRNO_TLSFAILURE + && ob->tls_tempfail_tryclear + && verify_check_given_host(&ob->hosts_require_tls, host) != OK + ) { log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " "to %s [%s] (not in hosts_require_tls)", host->name, host->address); first_addr = prepare_addresses(addrlist, host); - rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock, - expanded_hosts != NULL, &message_defer, TRUE); + rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + &message_defer, TRUE); if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL) write_logs(first_addr, host); - #ifdef EXPERIMENTAL_TPDA +# ifdef EXPERIMENTAL_EVENT if (rc == DEFER) - tpda_deferred(ob, first_addr, host); - #endif + deferred_event_raise(first_addr, host); +# endif } - #endif +#endif /*SUPPORT_TLS*/ } /* Delivery attempt finished */ @@ -3307,7 +3624,13 @@ int delete_flag = (rc != DEFER)? rf_delete : 0; if (retry_host_key == NULL) { - retry_host_key = ob->retry_include_ip_address? + BOOL incl_ip; + if (exp_bool(addrlist, US"transport", tblock->name, D_transport, + US"retry_include_ip_address", ob->retry_include_ip_address, + ob->expand_retry_include_ip_address, &incl_ip) != OK) + incl_ip = TRUE; /* error; use most-specific retry record */ + + retry_host_key = incl_ip ? string_sprintf("T:%S:%s%s", host->name, host->address, pistring) : string_sprintf("T:%S%s", host->name, pistring); } @@ -3349,7 +3672,13 @@ int delete_flag = message_defer? 0 : rf_delete; if (retry_message_key == NULL) { - retry_message_key = ob->retry_include_ip_address? + BOOL incl_ip; + if (exp_bool(addrlist, US"transport", tblock->name, D_transport, + US"retry_include_ip_address", ob->retry_include_ip_address, + ob->expand_retry_include_ip_address, &incl_ip) != OK) + incl_ip = TRUE; /* error; use most-specific retry record */ + + retry_message_key = incl_ip ? string_sprintf("T:%S:%s%s:%s", host->name, host->address, pistring, message_id) : string_sprintf("T:%S%s:%s", host->name, pistring, message_id); @@ -3364,16 +3693,12 @@ case, see if any of them are deferred. */ if (rc == OK) - { - for (addr = addrlist; addr != NULL; addr = addr->next) - { + for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return == DEFER) { some_deferred = TRUE; break; } - } - } /* If no addresses deferred or the result was ERROR, return. We do this for ERROR because a failing filter set-up or add_headers expansion is likely to diff -Nru exim4-4.84/src/transports/smtp.h exim4-4.86~RC4/src/transports/smtp.h --- exim4-4.84/src/transports/smtp.h 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/smtp.h 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Private structure for the private options and other private data. */ @@ -21,6 +21,10 @@ uschar *serialize_hosts; uschar *hosts_try_auth; uschar *hosts_require_auth; +#ifdef EXPERIMENTAL_DANE + uschar *hosts_try_dane; + uschar *hosts_require_dane; +#endif #ifndef DISABLE_PRDR uschar *hosts_try_prdr; #endif @@ -47,14 +51,17 @@ BOOL gethostbyname; BOOL dns_qualify_single; BOOL dns_search_parents; - uschar *dnssec_request_domains; - uschar *dnssec_require_domains; + dnssec_domains dnssec; BOOL delay_after_cutoff; BOOL hosts_override; BOOL hosts_randomize; BOOL keepalive; BOOL lmtp_ignore_quota; + uschar *expand_retry_include_ip_address; BOOL retry_include_ip_address; +#ifdef EXPERIMENTAL_SOCKS + uschar *socks_proxy; +#endif #ifdef SUPPORT_TLS uschar *tls_certificate; uschar *tls_crl; @@ -69,9 +76,7 @@ BOOL tls_tempfail_tryclear; uschar *tls_verify_hosts; uschar *tls_try_verify_hosts; -# ifdef EXPERIMENTAL_CERTNAMES uschar *tls_verify_cert_hostnames; -# endif #endif #ifndef DISABLE_DKIM uschar *dkim_domain; @@ -81,9 +86,6 @@ uschar *dkim_sign_headers; uschar *dkim_strict; #endif -#ifdef EXPERIMENTAL_TPDA - uschar *tpda_host_defer_action; -#endif } smtp_transport_options_block; /* Data for reading the private options. */ @@ -109,4 +111,9 @@ extern BOOL smtp_mail_auth_str(uschar *, unsigned, address_item *, smtp_transport_options_block *); +#ifdef EXPERMENTAL_SOCKS +extern int socks_sock_connect(host_item, int, int, uschar *, + transport_instance *, int); +#endif + /* End of transports/smtp.h */ diff -Nru exim4-4.84/src/transports/smtp_socks.c exim4-4.86~RC4/src/transports/smtp_socks.c --- exim4-4.84/src/transports/smtp_socks.c 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/transports/smtp_socks.c 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,312 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + +/* SOCKS version 5 proxy, client-mode */ + +#include "../exim.h" +#include "smtp.h" + +#ifdef EXPERIMENTAL_SOCKS /* entire file */ + +#ifndef nelem +# define nelem(arr) (sizeof(arr)/sizeof(*arr)) +#endif + + +/* Defaults */ +#define SOCKS_PORT 1080 +#define SOCKS_TIMEOUT 5 + +#define AUTH_NONE 0 +#define AUTH_NAME 2 /* user/password per RFC 1929 */ +#define AUTH_NAME_VER 1 + +struct socks_err + { + uschar * reason; + int errcode; + } socks_errs[] = + { + {NULL, 0}, + {US"general SOCKS server failure", EIO}, + {US"connection not allowed by ruleset", EACCES}, + {US"Network unreachable", ENETUNREACH}, + {US"Host unreachable", EHOSTUNREACH}, + {US"Connection refused", ECONNREFUSED}, + {US"TTL expired", ECANCELED}, + {US"Command not supported", EOPNOTSUPP}, + {US"Address type not supported", EAFNOSUPPORT} + }; + +typedef struct + { + uschar auth_type; /* RFC 1928 encoding */ + const uschar * auth_name; + const uschar * auth_pwd; + short port; + unsigned timeout; + } socks_opts; + +static void +socks_option_defaults(socks_opts * sob) +{ +sob->auth_type = AUTH_NONE; +sob->auth_name = US""; +sob->auth_pwd = US""; +sob->port = SOCKS_PORT; +sob->timeout = SOCKS_TIMEOUT; +} + +static void +socks_option(socks_opts * sob, const uschar * opt) +{ +const uschar * s; + +if (Ustrncmp(opt, "auth=", 5) == 0) + { + opt += 5; + if (Ustrcmp(opt, "none") == 0) sob->auth_type = AUTH_NONE; + else if (Ustrcmp(opt, "name") == 0) sob->auth_type = AUTH_NAME; + } +else if (Ustrncmp(opt, "name=", 5) == 0) + sob->auth_name = opt + 5; +else if (Ustrncmp(opt, "pass=", 5) == 0) + sob->auth_pwd = opt + 5; +else if (Ustrncmp(opt, "port=", 5) == 0) + sob->port = atoi(opt + 5); +else if (Ustrncmp(opt, "tmo=", 4) == 0) + sob->timeout = atoi(opt + 4); +return; +} + +static int +socks_auth(int fd, int method, socks_opts * sob, time_t tmo) +{ +uschar * s; +int len, i, j; + +switch(method) + { + default: + log_write(0, LOG_MAIN|LOG_PANIC, + "Unrecognised socks auth method %d", method); + return FAIL; + case AUTH_NONE: + return OK; + case AUTH_NAME: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" socks auth NAME '%s' '%s'\n", + sob->auth_name, sob->auth_pwd); + i = Ustrlen(sob->auth_name); + j = Ustrlen(sob->auth_pwd); + s = string_sprintf("%c%c%.255s%c%.255s", AUTH_NAME_VER, + i, sob->auth_name, j, sob->auth_pwd); + len = i + j + 3; + HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; ioptions_block; +const uschar * proxy_list; +const uschar * proxy_spec; +int sep = 0; +int fd; +time_t tmo; +const uschar * state; +uschar buf[24]; + +if (!timeout) timeout = 24*60*60; /* use 1 day for "indefinite" */ +tmo = time(NULL) + timeout; + +if (!(proxy_list = expand_string(ob->socks_proxy))) + { + log_write(0, LOG_MAIN|LOG_PANIC, "Bad expansion for socks_proxy in %s", + tb->name); + return -1; + } + +/* Loop over proxy list, trying in order until one works */ +while ((proxy_spec = string_nextinlist(&proxy_list, &sep, NULL, 0))) + { + const uschar * proxy_host; + int subsep = -' '; + host_item proxy; + int proxy_af; + union sockaddr_46 sin; + unsigned size; + socks_opts sob; + const uschar * option; + + if (!(proxy_host = string_nextinlist(&proxy_spec, &subsep, NULL, 0))) + { + /* paniclog config error */ + return -1; + } + + /*XXX consider global options eg. "hide socks_password = wibble" on the tpt */ + socks_option_defaults(&sob); + + /* extract any further per-proxy options */ + while ((option = string_nextinlist(&proxy_spec, &subsep, NULL, 0))) + socks_option(&sob, option); + + /* bodge up a host struct for the proxy */ + proxy.address = proxy_host; + proxy_af = Ustrchr(proxy_host, ':') ? AF_INET6 : AF_INET; + + if ((fd = smtp_sock_connect(&proxy, proxy_af, sob.port, + interface, tb, sob.timeout)) < 0) + continue; + + /* Do the socks protocol stuff */ + /* Send method-selection */ + state = US"method select"; + HDEBUG(D_transport|D_acl|D_v) debug_printf(" SOCKS>> 05 01 %02x\n", sob.auth_type); + buf[0] = 5; buf[1] = 1; buf[2] = sob.auth_type; + if (send(fd, buf, 3, 0) < 0) + goto snd_err; + + /* expect method response */ + if ( !fd_ready(fd, tmo-time(NULL)) + || read(fd, buf, 2) != 2 + ) + goto rcv_err; + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" SOCKS<< %02x %02x\n", buf[0], buf[1]); + if ( buf[0] != 5 + || socks_auth(fd, buf[1], &sob, tmo) != OK + ) + goto proxy_err; + + (void) ip_addr(&sin, host_af, host->address, port); + + /* send connect (ipver, ipaddr, port) */ + buf[0] = 5; buf[1] = 1; buf[2] = 0; buf[3] = host_af == AF_INET6 ? 4 : 1; +#if HAVE_IPV6 + if (host_af == AF_INET6) + { + memcpy(buf+4, &sin.v6.sin6_addr, sizeof(sin.v6.sin6_addr)); + memcpy(buf+4+sizeof(sin.v6.sin6_addr), + &sin.v6.sin6_port, sizeof(sin.v6.sin6_port)); + size = 4+sizeof(sin.v6.sin6_addr)+sizeof(sin.v6.sin6_port); + } + else +#endif + { + memcpy(buf+4, &sin.v4.sin_addr.s_addr, sizeof(sin.v4.sin_addr.s_addr)); + memcpy(buf+4+sizeof(sin.v4.sin_addr.s_addr), + &sin.v4.sin_port, sizeof(sin.v4.sin_port)); + size = 4+sizeof(sin.v4.sin_addr.s_addr)+sizeof(sin.v4.sin_port); + } + + state = US"connect"; + HDEBUG(D_transport|D_acl|D_v) + { + int i; + debug_printf(" SOCKS>>"); + for (i = 0; i>"); + for (i = 0; i nelem(socks_errs) ? NULL : socks_errs + buf[1]; + HDEBUG(D_transport|D_acl|D_v) + debug_printf(" proxy %s: %s\n", state, se ? se->reason : US"unknown error code received"); + errno = se ? se->errcode : EPROTO; + } + +rcv_err: + HDEBUG(D_transport|D_acl|D_v) debug_printf(" proxy rcv_err %s: %s\n", state, strerror(errno)); + if (!errno) errno = EPROTO; + else if (errno == ENOENT) errno = ECONNABORTED; + return -1; +} + +#endif /* entire file */ +/* vi: aw ai sw=2 +*/ diff -Nru exim4-4.84/src/transports/tf_maildir.c exim4-4.86~RC4/src/transports/tf_maildir.c --- exim4-4.84/src/transports/tf_maildir.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/transports/tf_maildir.c 2015-06-27 17:01:28.000000000 +0200 @@ -554,8 +554,8 @@ FALSE); (void)gettimeofday(&tv, NULL); - tempname = string_sprintf("%s/tmp/%lu.H%luP%lu.%s", path, tv.tv_sec, - tv.tv_usec, (long unsigned) getpid(), primary_hostname); + tempname = string_sprintf("%s/tmp/" TIME_T_FMT ".H%luP%lu.%s", + path, tv.tv_sec, tv.tv_usec, (long unsigned) getpid(), primary_hostname); fd = Uopen(tempname, O_RDWR|O_CREAT|O_EXCL, ob->mode ? ob->mode : 0600); if (fd >= 0) diff -Nru exim4-4.84/src/tree.c exim4-4.86~RC4/src/tree.c --- exim4-4.84/src/tree.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/tree.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for maintaining binary balanced trees and some associated @@ -328,7 +328,7 @@ */ tree_node * -tree_search(tree_node *p, uschar *name) +tree_search(tree_node *p, const uschar *name) { while (p != NULL) { diff -Nru exim4-4.84/src/utf8.c exim4-4.86~RC4/src/utf8.c --- exim4-4.84/src/utf8.c 1970-01-01 01:00:00.000000000 +0100 +++ exim4-4.86~RC4/src/utf8.c 2015-06-27 17:01:28.000000000 +0200 @@ -0,0 +1,195 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) Jeremy Harris 2015 */ +/* See the file NOTICE for conditions of use and distribution. */ + + +#include "exim.h" + +#ifdef EXPERIMENTAL_INTERNATIONAL + +#include +#include +#include + +BOOL +string_is_utf8(const uschar * s) +{ +uschar c; +while ((c = *s++)) if (c & 0x80) return TRUE; +return FALSE; +} + +/**************************************************/ +/* Domain conversions */ +/* the *err string pointer should be null before the call */ + +uschar * +string_domain_utf8_to_alabel(const uschar * utf8, uschar ** err) +{ +uschar * s1; +uschar * s; +int rc; + +s = US stringprep_utf8_nfkc_normalize(CCS utf8, -1); +if ( (rc = idna_to_ascii_8z(CCS s, CSS &s1, IDNA_ALLOW_UNASSIGNED)) + != IDNA_SUCCESS) + { + free(s); + if (err) *err = US idna_strerror(rc); + return NULL; + } +free(s); +s = string_copy(s1); +free(s1); +return s; +} + + + +uschar * +string_domain_alabel_to_utf8(const uschar * alabel, uschar ** err) +{ +uschar * s1; +uschar * s; +int rc; + +if ( (rc = idna_to_unicode_8z8z(CCS alabel, CSS &s1, IDNA_USE_STD3_ASCII_RULES)) + != IDNA_SUCCESS) + { + if (err) *err = US idna_strerror(rc); + return NULL; + } +s = string_copy(s1); +free(s1); +return s; +} + +/**************************************************/ +/* localpart conversions */ +/* the *err string pointer should be null before the call */ + + +uschar * +string_localpart_utf8_to_alabel(const uschar * utf8, uschar ** err) +{ +size_t ucs4_len; +punycode_uint * p; +size_t p_len; +uschar * res; +int rc; + +if (!string_is_utf8(utf8)) return string_copy(utf8); + +p = (punycode_uint *) stringprep_utf8_to_ucs4(CCS utf8, -1, &ucs4_len); +p_len = ucs4_len*4; /* this multiplier is pure guesswork */ +res = store_get(p_len+5); + +res[0] = 'x'; res[1] = 'n'; res[2] = res[3] = '-'; + +if ((rc = punycode_encode(ucs4_len, p, NULL, &p_len, CS res+4)) != PUNYCODE_SUCCESS) + { + DEBUG(D_expand) debug_printf("l_u2a: bad '%s'\n", punycode_strerror(rc)); + free(p); + if (err) *err = US punycode_strerror(rc); + return NULL; + } +p_len += 4; +free(p); +res[p_len] = '\0'; +return res; +} + + +uschar * +string_localpart_alabel_to_utf8(const uschar * alabel, uschar ** err) +{ +size_t p_len = Ustrlen(alabel); +punycode_uint * p; +uschar * s; +uschar * res; +int rc; + +if (alabel[0] != 'x' || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-') + { + if (err) *err = US"bad alabel prefix"; + return NULL; + } + +p_len -= 4; +p = (punycode_uint *) store_get((p_len+1) * sizeof(*p)); + +if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUCCESS) + { + if (err) *err = US punycode_strerror(rc); + return NULL; + } + +s = stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len); +res = string_copyn(s, p_len); +free(s); +return res; +} + + +/**************************************************/ +/* whole address conversion */ +/* the *err string pointer should be null before the call */ + +uschar * +string_address_utf8_to_alabel(const uschar * utf8, uschar ** err) +{ +const uschar * s; +uschar * l; +uschar * d; + +if (!*utf8) return string_copy(utf8); + +DEBUG(D_expand) debug_printf("addr from utf8 <%s>", utf8); + +for (s = utf8; *s; s++) + if (*s == '@') + { + l = string_copyn(utf8, s - utf8); + if ( (l = string_localpart_utf8_to_alabel(l, err), err && *err) + || (d = string_domain_utf8_to_alabel(++s, err), err && *err) + ) + return NULL; + l = string_sprintf("%s@%s", l, d); + DEBUG(D_expand) debug_printf(" -> <%s>\n", l); + return l; + } + +l = string_localpart_utf8_to_alabel(utf8, err); +DEBUG(D_expand) debug_printf(" -> <%s>\n", l); +return l; +} + + + +/************************************************* +* Report the library versions. * +*************************************************/ + +/* See a description in tls-openssl.c for an explanation of why this exists. + +Arguments: a FILE* to print the results to +Returns: nothing +*/ + +void +utf8_version_report(FILE *f) +{ +fprintf(f, "Library version: IDN: Compile: %s\n" + " Runtime: %s\n", + STRINGPREP_VERSION, + stringprep_check_version(NULL)); +} + +#endif /* whole file */ + +/* vi: aw ai sw=2 +*/ +/* End of utf8.c */ diff -Nru exim4-4.84/src/verify.c exim4-4.86~RC4/src/verify.c --- exim4-4.84/src/verify.c 2014-08-09 14:44:29.000000000 +0200 +++ exim4-4.86~RC4/src/verify.c 2015-06-27 17:01:28.000000000 +0200 @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2014 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with verifying things. The original code for callout @@ -14,7 +14,6 @@ #define CUTTHROUGH_CMD_TIMEOUT 30 /* timeout for cutthrough-routing calls */ #define CUTTHROUGH_DATA_TIMEOUT 60 /* timeout for cutthrough-routing calls */ -address_item cutthrough_addr; static smtp_outblock ctblock; uschar ctbuffer[8192]; @@ -39,6 +38,7 @@ #define MT_NOT 1 #define MT_ALL 2 +static uschar cutthrough_response(char, uschar **); /************************************************* @@ -58,7 +58,7 @@ */ static dbdata_callout_cache * -get_callout_cache_record(open_db *dbm_file, uschar *key, uschar *type, +get_callout_cache_record(open_db *dbm_file, const uschar *key, uschar *type, int positive_expire, int negative_expire) { BOOL negative; @@ -70,7 +70,7 @@ if (cache_record == NULL) { - HDEBUG(D_verify) debug_printf("callout cache: no %s record found\n", type); + HDEBUG(D_verify) debug_printf("callout cache: no %s record found for %s\n", type, key); return NULL; } @@ -84,7 +84,7 @@ if (now - cache_record->time_stamp > expire) { - HDEBUG(D_verify) debug_printf("callout cache: %s record expired\n", type); + HDEBUG(D_verify) debug_printf("callout cache: %s record expired for %s\n", type, key); return NULL; } @@ -111,7 +111,7 @@ cache_record->random_result = ccache_unknown; } -HDEBUG(D_verify) debug_printf("callout cache: found %s record\n", type); +HDEBUG(D_verify) debug_printf("callout cache: found %s record for %s\n", type, key); return cache_record; } @@ -164,7 +164,7 @@ uschar *address_key; uschar *from_address; uschar *random_local_part = NULL; -uschar *save_deliver_domain = deliver_domain; +const uschar *save_deliver_domain = deliver_domain; uschar **failure_ptr = is_recipient? &recipient_verify_failure : &sender_verify_failure; open_db dbblock; @@ -173,6 +173,9 @@ dbdata_callout_cache_address new_address_record; host_item *host; time_t callout_start_time; +#ifdef EXPERIMENTAL_INTERNATIONAL +BOOL utf8_offered = FALSE; +#endif new_domain_record.result = ccache_unknown; new_domain_record.postmaster_result = ccache_unknown; @@ -189,12 +192,12 @@ if (is_recipient) { - if ((options & vopt_callout_recipsender) != 0) + if (options & vopt_callout_recipsender) { address_key = string_sprintf("%s/<%s>", addr->address, sender_address); from_address = sender_address; } - else if ((options & vopt_callout_recippmaster) != 0) + else if (options & vopt_callout_recippmaster) { address_key = string_sprintf("%s/", addr->address, qualify_domain_sender); @@ -388,12 +391,9 @@ log the fact, but carry on without randomming. */ if (callout_random && callout_random_local_part != NULL) - { - random_local_part = expand_string(callout_random_local_part); - if (random_local_part == NULL) + if (!(random_local_part = expand_string(callout_random_local_part))) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " "callout_random_local_part: %s", expand_string_message); - } /* Default the connect and overall callout timeouts if not set, and record the time we are starting so that we can enforce it. */ @@ -410,6 +410,113 @@ if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush(); +/* cutthrough-multi: if a nonfirst rcpt has the same routing as the first, +and we are holding a cutthrough conn open, we can just append the rcpt to +that conn for verification purposes (and later delivery also). Simplest +coding means skipping this whole loop and doing the append separately. + +We will need to remember it has been appended so that rcpt-acl tail code +can do it there for the non-rcpt-verify case. For this we keep an addresscount. +*/ + + /* Can we re-use an open cutthrough connection? */ + if ( cutthrough.fd >= 0 + && (options & (vopt_callout_recipsender | vopt_callout_recippmaster)) + == vopt_callout_recipsender + && !random_local_part + && !pm_mailfrom + ) + { + if (addr->transport == cutthrough.addr.transport) + for (host = host_list; host; host = host->next) + if (Ustrcmp(host->address, cutthrough.host.address) == 0) + { + int host_af; + uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ + int port = 25; + + deliver_host = host->name; + deliver_host_address = host->address; + deliver_host_port = host->port; + deliver_domain = addr->domain; + transport_name = addr->transport->name; + + host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6; + + if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, + US"callout") || + !smtp_get_port(tf->port, addr, &port, US"callout")) + log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, + addr->message); + + if ( ( interface == cutthrough.interface + || ( interface + && cutthrough.interface + && Ustrcmp(interface, cutthrough.interface) == 0 + ) ) + && port == cutthrough.host.port + ) + { + uschar * resp; + + /* Match! Send the RCPT TO, append the addr, set done */ + done = + smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n", + transport_rcpt_address(addr, + (addr->transport == NULL)? FALSE : + addr->transport->rcpt_include_affixes)) >= 0 && + cutthrough_response('2', &resp) == '2'; + + /* This would go horribly wrong if a callout fail was ignored by ACL. + We punt by abandoning cutthrough on a reject, like the + first-rcpt does. */ + + if (done) + { + address_item * na = store_get(sizeof(address_item)); + *na = cutthrough.addr; + cutthrough.addr = *addr; + cutthrough.addr.host_used = &cutthrough.host; + cutthrough.addr.next = na; + + cutthrough.nrcpt++; + } + else + { + cancel_cutthrough_connection("recipient rejected"); + if (errno == ETIMEDOUT) + { + HDEBUG(D_verify) debug_printf("SMTP timeout\n"); + } + else if (errno == 0) + { + if (*resp == 0) + Ustrcpy(resp, US"connection dropped"); + + addr->message = + string_sprintf("response to \"%s\" from %s [%s] was: %s", + big_buffer, host->name, host->address, + string_printing(resp)); + + addr->user_message = + string_sprintf("Callout verification failed:\n%s", resp); + + /* Hard rejection ends the process */ + + if (resp[0] == '5') /* Address rejected */ + { + yield = FAIL; + done = TRUE; + } + } + } + } + break; + } + if (!done) + cancel_cutthrough_connection("incompatible connection"); + } + /* Now make connections to the hosts and do real callouts. The list of hosts is passed in as an argument. */ @@ -426,6 +533,11 @@ BOOL esmtp; BOOL suppress_tls = FALSE; uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + BOOL dane = FALSE; + BOOL dane_required; + dns_answer tlsa_dnsa; +#endif uschar inbuffer[4096]; uschar outbuffer[1024]; uschar responsebuffer[4096]; @@ -462,11 +574,14 @@ deliver_host = host->name; deliver_host_address = host->address; + deliver_host_port = host->port; deliver_domain = addr->domain; + transport_name = addr->transport->name; - if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, - US"callout") || - !smtp_get_port(tf->port, addr, &port, US"callout")) + if ( !smtp_get_interface(tf->interface, host_af, addr, NULL, &interface, + US"callout") + || !smtp_get_port(tf->port, addr, &port, US"callout") + ) log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, addr->message); @@ -492,26 +607,57 @@ outblock.cmd_count = 0; outblock.authenticating = FALSE; - /* Reset the parameters of a TLS session */ - tls_out.cipher = tls_out.peerdn = NULL; - /* Connect to the host; on failure, just loop for the next one, but we set the error for the last one. Use the callout_connect timeout. */ tls_retry_connection: + /* Reset the parameters of a TLS session */ + tls_out.cipher = tls_out.peerdn = tls_out.peercert = NULL; + inblock.sock = outblock.sock = - smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL); - /* reconsider DSCP here */ + smtp_connect(host, host_af, port, interface, callout_connect, + addr->transport); if (inblock.sock < 0) { addr->message = string_sprintf("could not connect to %s [%s]: %s", host->name, host->address, strerror(errno)); + transport_name = NULL; deliver_host = deliver_host_address = NULL; deliver_domain = save_deliver_domain; continue; } +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + { + int rc; + + tls_out.dane_verified = FALSE; + tls_out.tlsa_usage = 0; + + dane_required = + verify_check_given_host(&ob->hosts_require_dane, host) == OK; + + if (host->dnssec == DS_YES) + { + if( ( dane_required + || verify_check_given_host(&ob->hosts_try_dane, host) == OK + ) + && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK + ) + return rc; + } + else if (dane_required) + { + log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name); + return FAIL; + } + + if (dane) + ob->tls_tempfail_tryclear = FALSE; + } +#endif /*DANE*/ + /* Expand the helo_data string to find the host name to use. */ if (tf->helo_data != NULL) @@ -533,28 +679,42 @@ /* Unless ssl-on-connect, wait for the initial greeting */ smtps_redo_greeting: - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (!smtps || (smtps && tls_out.active >= 0)) - #endif +#endif + { if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout))) goto RESPONSE_FAILED; +#ifdef EXPERIMENTAL_EVENT + lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes" + : host->dnssec==DS_NO ? US"no" : NULL; + if (event_raise(addr->transport->event_action, + US"smtp:connect", responsebuffer)) + { + lookup_dnssec_authenticated = NULL; + /* Logging? Debug? */ + goto RESPONSE_FAILED; + } + lookup_dnssec_authenticated = NULL; +#endif + } + /* Not worth checking greeting line for ESMTP support */ - if (!(esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL, - host->name, host->address, NULL) != OK)) + if (!(esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK)) DEBUG(D_transport) debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n"); tls_redo_helo: - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (smtps && tls_out.active < 0) /* ssl-on-connect, first pass */ { tls_offered = TRUE; ob->tls_tempfail_tryclear = FALSE; } - else /* all other cases */ - #endif + else /* all other cases */ +#endif { esmtp_retry: @@ -568,26 +728,26 @@ done= FALSE; goto RESPONSE_FAILED; } - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_offered = FALSE; - #endif +#endif esmtp = FALSE; goto esmtp_retry; /* fallback to HELO */ } /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS if (esmtp && !suppress_tls && tls_out.active < 0) - { - if (regex_STARTTLS == NULL) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); + { + if (regex_STARTTLS == NULL) regex_STARTTLS = + regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); - tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer, - Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0; + tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer, + Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0; } else tls_offered = FALSE; - #endif +#endif } /* If TLS is available on this connection attempt to @@ -598,12 +758,10 @@ the client not be required to use TLS. If the response is bad, copy the buffer for error analysis. */ - #ifdef SUPPORT_TLS - if (tls_offered && - verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name, - host->address, NULL) != OK && - verify_check_this_host(&(ob->hosts_verify_avoid_tls), NULL, host->name, - host->address, NULL) != OK +#ifdef SUPPORT_TLS + if ( tls_offered + && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK + && verify_check_given_host(&ob->hosts_verify_avoid_tls, host) != OK ) { uschar buffer2[4096]; @@ -623,11 +781,11 @@ { if (errno != 0 || buffer2[0] == 0 || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)) - { - Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer)); - done= FALSE; - goto RESPONSE_FAILED; - } + { + Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer)); + done= FALSE; + goto RESPONSE_FAILED; + } } /* STARTTLS accepted or ssl-on-connect: try to negotiate a TLS session. */ @@ -636,30 +794,59 @@ int oldtimeout = ob->command_timeout; int rc; + tls_negotiate: ob->command_timeout = callout; - rc = tls_client_start(inblock.sock, host, addr, ob); + rc = tls_client_start(inblock.sock, host, addr, addr->transport +# ifdef EXPERIMENTAL_DANE + , dane ? &tlsa_dnsa : NULL +# endif + ); ob->command_timeout = oldtimeout; - /* TLS negotiation failed; give an error. Try in clear on a new connection, - if the options permit it for this host. */ + /* TLS negotiation failed; give an error. Try in clear on a new + connection, if the options permit it for this host. */ if (rc != OK) { - if (rc == DEFER && ob->tls_tempfail_tryclear && !smtps && - verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) != OK) - { - (void)close(inblock.sock); - log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted " - "to %s [%s] (not in hosts_require_tls)", host->name, host->address); - suppress_tls = TRUE; - goto tls_retry_connection; - } - /*save_errno = ERRNO_TLSFAILURE;*/ - /*message = US"failure while setting up TLS session";*/ - send_quit = FALSE; - done= FALSE; - goto TLS_FAILED; - } + if (rc == DEFER) + { + (void)close(inblock.sock); +# ifdef EXPERIMENTAL_EVENT + (void) event_raise(addr->transport->event_action, + US"tcp:close", NULL); +# endif +# ifdef EXPERIMENTAL_DANE + if (dane) + { + if (!dane_required) + { + log_write(0, LOG_MAIN, "DANE attempt failed;" + " trying CA-root TLS to %s [%s] (not in hosts_require_dane)", + host->name, host->address); + dane = FALSE; + goto tls_negotiate; + } + } + else +# endif + if ( ob->tls_tempfail_tryclear + && !smtps + && verify_check_given_host(&ob->hosts_require_tls, host) != OK + ) + { + log_write(0, LOG_MAIN, "TLS session failure:" + " delivering unencrypted to %s [%s] (not in hosts_require_tls)", + host->name, host->address); + suppress_tls = TRUE; + goto tls_retry_connection; + } + } + + /*save_errno = ERRNO_TLSFAILURE;*/ + /*message = US"failure while setting up TLS session";*/ + send_quit = FALSE; + done= FALSE; + goto TLS_FAILED; + } /* TLS session is set up. Copy info for logging. */ addr->cipher = tls_out.cipher; @@ -667,7 +854,7 @@ /* For SMTPS we need to wait for the initial OK response, then do HELO. */ if (smtps) - goto smtps_redo_greeting; + goto smtps_redo_greeting; /* For STARTTLS we need to redo EHLO */ goto tls_redo_helo; @@ -676,18 +863,24 @@ /* If the host is required to use a secure channel, ensure that we have one. */ if (tls_out.active < 0) - if (verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name, - host->address, NULL) == OK) + if ( +# ifdef EXPERIMENTAL_DANE + dane || +# endif + verify_check_given_host(&ob->hosts_require_tls, host) == OK + ) { /*save_errno = ERRNO_TLSREQUIRED;*/ - log_write(0, LOG_MAIN, "a TLS session is required for %s [%s], but %s", + log_write(0, LOG_MAIN, + "H=%s [%s]: a TLS session is required for this host, but %s", host->name, host->address, - tls_offered? "an attempt to start TLS failed" : "the server did not offer TLS support"); + tls_offered ? "an attempt to start TLS failed" + : "the server did not offer TLS support"); done= FALSE; goto TLS_FAILED; } - #endif /*SUPPORT_TLS*/ +#endif /*SUPPORT_TLS*/ done = TRUE; /* so far so good; have response to HELO */ @@ -695,20 +888,20 @@ /* For now, transport_filter by cutthrough-delivery is not supported */ /* Need proper integration with the proper transport mechanism. */ - if (cutthrough_delivery) + if (cutthrough.delivery) { if (addr->transport->filter_command) { - cutthrough_delivery= FALSE; + cutthrough.delivery = FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n"); } - #ifndef DISABLE_DKIM +#ifndef DISABLE_DKIM if (ob->dkim_domain) { - cutthrough_delivery= FALSE; + cutthrough.delivery = FALSE; HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n"); } - #endif +#endif } SEND_FAILED: @@ -730,6 +923,40 @@ } } +#ifdef EXPERIMENTAL_INTERNATIONAL + else if ( addr->prop.utf8_msg + && !addr->prop.utf8_downcvt + && !( esmtp + && ( regex_UTF8 + || ( (regex_UTF8 = regex_must_compile( + US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE)), + TRUE + ) ) + && ( (utf8_offered = pcre_exec(regex_UTF8, NULL, + CS responsebuffer, Ustrlen(responsebuffer), + 0, PCRE_EOPT, NULL, 0) >= 0) + || addr->prop.utf8_downcvt_maybe + ) ) ) + { + HDEBUG(D_acl|D_v) debug_printf("utf8 required but not offered\n"); + errno = ERRNO_UTF8_FWD; + setflag(addr, af_verify_nsfail); + done = FALSE; + } + else if ( addr->prop.utf8_msg + && (addr->prop.utf8_downcvt || !utf8_offered) + && (setflag(addr, af_utf8_downcvt), + from_address = string_address_utf8_to_alabel(from_address, + &addr->message), + addr->message + ) ) + { + errno = ERRNO_EXPANDFAIL; + setflag(addr, af_verify_nsfail); + done = FALSE; + } +#endif + /* If we haven't authenticated, but are required to, give up. */ /* Try to AUTH */ @@ -747,7 +974,13 @@ ( (addr->auth_sndr = client_authenticated_sender), /* Send the MAIL command */ - (smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>%s\r\n", + (smtp_write_command(&outblock, FALSE, +#ifdef EXPERIMENTAL_INTERNATIONAL + addr->prop.utf8_msg && !addr->prop.utf8_downcvt + ? "MAIL FROM:<%s>%s SMTPUTF8\r\n" + : +#endif + "MAIL FROM:<%s>%s\r\n", from_address, responsebuffer) >= 0) ) && @@ -786,6 +1019,23 @@ else { + const uschar * rcpt_domain = addr->domain; + +#ifdef EXPERIMENTAL_INTERNATIONAL + uschar * errstr = NULL; + if ( testflag(addr, af_utf8_downcvt) + && (rcpt_domain = string_domain_utf8_to_alabel(rcpt_domain, + &errstr), errstr) + ) + { + addr->message = errstr; + errno = ERRNO_EXPANDFAIL; + setflag(addr, af_verify_nsfail); + done = FALSE; + rcpt_domain = US""; /*XXX errorhandling! */ + } +#endif + new_domain_record.result = (old_domain_cache_result == ccache_reject_mfnull)? ccache_reject_mfnull: ccache_accept; @@ -798,7 +1048,7 @@ BOOL random_ok = smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s@%.1000s>\r\n", random_local_part, - addr->domain) >= 0 && + rcpt_domain) >= 0 && smtp_read_response(&inblock, randombuffer, sizeof(randombuffer), '2', callout); @@ -809,16 +1059,21 @@ /* If accepted, we aren't going to do any further tests below. */ if (random_ok) - { new_domain_record.random_result = ccache_accept; - } /* Otherwise, cache a real negative response, and get back to the right state to send RCPT. Unless there's some problem such as a dropped - connection, we expect to succeed, because the commands succeeded above. */ + connection, we expect to succeed, because the commands succeeded above. + However, some servers drop the connection after responding to an + invalid recipient, so on (any) error we drop and remake the connection. + */ else if (errno == 0) { + /* This would be ok for 1st rcpt a cutthrough, but no way to + handle a subsequent. So refuse to support any */ + cancel_cutthrough_connection("random-recipient"); + if (randombuffer[0] == '5') new_domain_record.random_result = ccache_reject; @@ -827,10 +1082,32 @@ smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout) && - smtp_write_command(&outblock, FALSE, "MAIL FROM:<%s>\r\n", + smtp_write_command(&outblock, FALSE, +#ifdef EXPERIMENTAL_INTERNATIONAL + addr->prop.utf8_msg && !addr->prop.utf8_downcvt + ? "MAIL FROM:<%s> SMTPUTF8\r\n" + : +#endif + "MAIL FROM:<%s>\r\n", from_address) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout); + + if (!done) + { + HDEBUG(D_acl|D_v) + debug_printf("problem after random/rset/mfrom; reopen conn\n"); + random_local_part = NULL; +#ifdef SUPPORT_TLS + tls_close(FALSE, TRUE); +#endif + (void)close(inblock.sock); +#ifdef EXPERIMENTAL_EVENT + (void) event_raise(addr->transport->event_action, + US"tcp:close", NULL); +#endif + goto tls_retry_connection; + } } else done = FALSE; /* Some timeout/connection problem */ } /* Random check */ @@ -843,11 +1120,27 @@ /* Get the rcpt_include_affixes flag from the transport if there is one, but assume FALSE if there is not. */ + uschar * rcpt = transport_rcpt_address(addr, + addr->transport ? addr->transport->rcpt_include_affixes : FALSE); + +#ifdef EXPERIMENTAL_INTERNATIONAL + /*XXX should the conversion be moved into transport_rcpt_address() ? */ + uschar * dummy_errstr = NULL; + if ( testflag(addr, af_utf8_downcvt) + && (rcpt = string_address_utf8_to_alabel(rcpt, &dummy_errstr), + dummy_errstr + ) ) + { + errno = ERRNO_EXPANDFAIL; + *failure_ptr = US"recipient"; + done = FALSE; + } + else +#endif + done = smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s>\r\n", - transport_rcpt_address(addr, - (addr->transport == NULL)? FALSE : - addr->transport->rcpt_include_affixes)) >= 0 && + rcpt) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout); @@ -864,8 +1157,10 @@ if (done && pm_mailfrom != NULL) { - /*XXX not suitable for cutthrough - sequencing problems */ - cutthrough_delivery= FALSE; + /* Could possibly shift before main verify, just above, and be ok + for cutthrough. But no way to handle a subsequent rcpt, so just + refuse any */ + cancel_cutthrough_connection("postmaster verify"); HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n"); done = @@ -882,7 +1177,7 @@ (( smtp_write_command(&outblock, FALSE, - "RCPT TO:\r\n", addr->domain) >= 0 && + "RCPT TO:\r\n", rcpt_domain) >= 0 && smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout) ) @@ -933,6 +1228,21 @@ HDEBUG(D_verify) debug_printf("SMTP timeout\n"); send_quit = FALSE; } +#ifdef EXPERIMENTAL_INTERNATIONAL + else if (errno == ERRNO_UTF8_FWD) + { + extern int acl_where; /* src/acl.c */ + errno = 0; + addr->message = string_sprintf( + "response to \"%s\" from %s [%s] did not include SMTPUTF8", + big_buffer, host->name, host->address); + addr->user_message = acl_where == ACL_WHERE_RCPT + ? US"533 mailbox name not allowed" + : US"550 mailbox unavailable"; + yield = FAIL; + done = TRUE; + } +#endif else if (errno == 0) { if (*responsebuffer == 0) Ustrcpy(responsebuffer, US"connection dropped"); @@ -960,32 +1270,35 @@ /* End the SMTP conversation and close the connection. */ - /* Cutthrough - on a successfull connect and recipient-verify with use-sender - and we have no cutthrough conn so far + /* Cutthrough - on a successfull connect and recipient-verify with + use-sender and we are 1st rcpt and have no cutthrough conn so far here is where we want to leave the conn open */ - if ( cutthrough_delivery + if ( cutthrough.delivery + && rcpt_count == 1 && done && yield == OK && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender && !random_local_part && !pm_mailfrom - && cutthrough_fd < 0 + && cutthrough.fd < 0 + && !lmtp ) { - cutthrough_fd= outblock.sock; /* We assume no buffer in use in the outblock */ - cutthrough_addr = *addr; /* Save the address_item for later logging */ - cutthrough_addr.next = NULL; - cutthrough_addr.host_used = store_get(sizeof(host_item)); - cutthrough_addr.host_used->name = host->name; - cutthrough_addr.host_used->address = host->address; - cutthrough_addr.host_used->port = port; + cutthrough.fd = outblock.sock; /* We assume no buffer in use in the outblock */ + cutthrough.nrcpt = 1; + cutthrough.interface = interface; + cutthrough.host = *host; + cutthrough.addr = *addr; /* Save the address_item for later logging */ + cutthrough.addr.next = NULL; + cutthrough.addr.host_used = &cutthrough.host; if (addr->parent) - *(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent; + *(cutthrough.addr.parent = store_get(sizeof(address_item))) = + *addr->parent; ctblock.buffer = ctbuffer; ctblock.buffersize = sizeof(ctbuffer); ctblock.ptr = ctbuffer; /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */ - ctblock.sock = cutthrough_fd; + ctblock.sock = cutthrough.fd; } else { @@ -994,10 +1307,14 @@ cancel_cutthrough_connection("multiple verify calls"); if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n"); - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS tls_close(FALSE, TRUE); - #endif +#endif (void)close(inblock.sock); +#ifdef EXPERIMENTAL_EVENT + (void) event_raise(addr->transport->event_action, + US"tcp:close", NULL); +#endif } } /* Loop through all hosts, while !done */ @@ -1106,7 +1423,8 @@ get rewritten. */ addr2 = *addr; -HDEBUG(D_acl) debug_printf("----------- start cutthrough setup ------------\n"); +HDEBUG(D_acl) debug_printf("----------- %s cutthrough setup ------------\n", + rcpt_count > 1 ? "more" : "start"); (void) verify_address(&addr2, NULL, vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache, CUTTHROUGH_CMD_TIMEOUT, -1, -1, @@ -1121,14 +1439,14 @@ static BOOL cutthrough_send(int n) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return TRUE; if( #ifdef SUPPORT_TLS - (tls_out.active == cutthrough_fd) ? tls_write(FALSE, ctblock.buffer, n) : + (tls_out.active == cutthrough.fd) ? tls_write(FALSE, ctblock.buffer, n) : #endif - send(cutthrough_fd, ctblock.buffer, n, 0) > 0 + send(cutthrough.fd, ctblock.buffer, n, 0) > 0 ) { transport_count += n; @@ -1160,7 +1478,7 @@ BOOL cutthrough_puts(uschar * cp, int n) { -if (cutthrough_fd < 0) return TRUE; +if (cutthrough.fd < 0) return TRUE; if (_cutthrough_puts(cp, n)) return TRUE; cancel_cutthrough_connection("transmit failed"); return FALSE; @@ -1168,7 +1486,7 @@ static BOOL -_cutthrough_flush_send( void ) +_cutthrough_flush_send(void) { int n= ctblock.ptr-ctblock.buffer; @@ -1181,7 +1499,7 @@ /* Send out any bufferred output. Return boolean success. */ BOOL -cutthrough_flush_send( void ) +cutthrough_flush_send(void) { if (_cutthrough_flush_send()) return TRUE; cancel_cutthrough_connection("transmit failed"); @@ -1190,7 +1508,7 @@ BOOL -cutthrough_put_nl( void ) +cutthrough_put_nl(void) { return cutthrough_puts(US"\r\n", 2); } @@ -1208,7 +1526,7 @@ inblock.buffersize = sizeof(inbuffer); inblock.ptr = inbuffer; inblock.ptrend = inbuffer; -inblock.sock = cutthrough_fd; +inblock.sock = cutthrough.fd; /* this relies on (inblock.sock == tls_out.active) */ if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT)) cancel_cutthrough_connection("target timeout on read"); @@ -1216,7 +1534,7 @@ if(copy != NULL) { uschar * cp; - *copy= cp= string_copy(responsebuffer); + *copy = cp = string_copy(responsebuffer); /* Trim the trailing end of line */ cp += Ustrlen(responsebuffer); if(cp > *copy && cp[-1] == '\n') *--cp = '\0'; @@ -1229,9 +1547,9 @@ /* Negotiate dataphase with the cutthrough target, returning success boolean */ BOOL -cutthrough_predata( void ) +cutthrough_predata(void) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return FALSE; HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> DATA\n"); @@ -1262,9 +1580,9 @@ /* Expands newlines to wire format (CR,NL). */ /* Also sends header-terminating blank line. */ BOOL -cutthrough_headers_send( void ) +cutthrough_headers_send(void) { -if(cutthrough_fd < 0) +if(cutthrough.fd < 0) return FALSE; /* We share a routine with the mainline transport to handle header add/remove/rewrites, @@ -1272,10 +1590,12 @@ */ HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n"); -if (!transport_headers_send(&cutthrough_addr, cutthrough_fd, - cutthrough_addr.transport->add_headers, cutthrough_addr.transport->remove_headers, +if (!transport_headers_send(&cutthrough.addr, cutthrough.fd, + cutthrough.addr.transport->add_headers, + cutthrough.addr.transport->remove_headers, &cutthrough_write_chunk, TRUE, - cutthrough_addr.transport->rewrite_rules, cutthrough_addr.transport->rewrite_existflags)) + cutthrough.addr.transport->rewrite_rules, + cutthrough.addr.transport->rewrite_existflags)) return FALSE; HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n"); @@ -1284,9 +1604,9 @@ static void -close_cutthrough_connection( const char * why ) +close_cutthrough_connection(const char * why) { -if(cutthrough_fd >= 0) +if(cutthrough.fd >= 0) { /* We could be sending this after a bunch of data, but that is ok as the only way to cancel the transfer in dataphase is to drop the tcp @@ -1301,18 +1621,18 @@ #ifdef SUPPORT_TLS tls_close(FALSE, TRUE); #endif - (void)close(cutthrough_fd); - cutthrough_fd= -1; + (void)close(cutthrough.fd); + cutthrough.fd = -1; HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown (%s) ------------\n", why); } ctblock.ptr = ctbuffer; } void -cancel_cutthrough_connection( const char * why ) +cancel_cutthrough_connection(const char * why) { close_cutthrough_connection(why); -cutthrough_delivery= FALSE; +cutthrough.delivery = FALSE; } @@ -1324,33 +1644,45 @@ Return smtp response-class digit. */ uschar * -cutthrough_finaldot( void ) +cutthrough_finaldot(void) { +uschar res; +address_item * addr; HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP>> .\n"); /* Assume data finshed with new-line */ -if(!cutthrough_puts(US".", 1) || !cutthrough_put_nl() || !cutthrough_flush_send()) - return cutthrough_addr.message; +if( !cutthrough_puts(US".", 1) + || !cutthrough_put_nl() + || !cutthrough_flush_send() + ) + return cutthrough.addr.message; -switch(cutthrough_response('2', &cutthrough_addr.message)) +res = cutthrough_response('2', &cutthrough.addr.message); +for (addr = &cutthrough.addr; addr; addr = addr->next) { - case '2': - delivery_log(LOG_MAIN, &cutthrough_addr, (int)'>', NULL); - close_cutthrough_connection("delivered"); - break; + addr->message = cutthrough.addr.message; + switch(res) + { + case '2': + delivery_log(LOG_MAIN, addr, (int)'>', NULL); + close_cutthrough_connection("delivered"); + break; - case '4': - delivery_log(LOG_MAIN, &cutthrough_addr, 0, US"tmp-reject from cutthrough after DATA:"); - break; + case '4': + delivery_log(LOG_MAIN, addr, 0, + US"tmp-reject from cutthrough after DATA:"); + break; - case '5': - delivery_log(LOG_MAIN|LOG_REJECT, &cutthrough_addr, 0, US"rejected after DATA:"); - break; + case '5': + delivery_log(LOG_MAIN|LOG_REJECT, addr, 0, + US"rejected after DATA:"); + break; - default: - break; + default: + break; + } } - return cutthrough_addr.message; +return cutthrough.addr.message; } @@ -1381,7 +1713,7 @@ vaddr->user_message = addr->user_message; vaddr->basic_errno = addr->basic_errno; vaddr->more_errno = addr->more_errno; - vaddr->p.address_data = addr->p.address_data; + vaddr->prop.address_data = addr->prop.address_data; copyflag(vaddr, addr, af_pass_message); } return yield; @@ -1642,8 +1974,8 @@ /* Just in case some router parameter refers to it. */ - return_path = (addr->p.errors_address != NULL)? - addr->p.errors_address : sender_address; + return_path = (addr->prop.errors_address != NULL)? + addr->prop.errors_address : sender_address; /* Split the address into domain and local part, handling the %-hack if necessary, and then route it. While routing a sender address, set @@ -1703,7 +2035,7 @@ if (tf.hosts != NULL && (host_list == NULL || tf.hosts_override)) { uschar *s; - uschar *save_deliver_domain = deliver_domain; + const uschar *save_deliver_domain = deliver_domain; uschar *save_deliver_localpart = deliver_localpart; host_list = NULL; /* Ignore the router's hosts */ @@ -1723,7 +2055,6 @@ else { int flags; - uschar *canonical_name; host_item *host, *nexthost; host_build_hostlist(&host_list, s, tf.hosts_randomize); @@ -1742,21 +2073,20 @@ nexthost = host->next; if (tf.gethostbyname || string_is_ip_address(host->name, NULL) != 0) - (void)host_find_byname(host, NULL, flags, &canonical_name, TRUE); + (void)host_find_byname(host, NULL, flags, NULL, TRUE); else { - uschar * d_request = NULL, * d_require = NULL; + dnssec_domains * dnssec_domains = NULL; if (Ustrcmp(addr->transport->driver_name, "smtp") == 0) { smtp_transport_options_block * ob = (smtp_transport_options_block *) addr->transport->options_block; - d_request = ob->dnssec_request_domains; - d_require = ob->dnssec_require_domains; + dnssec_domains = &ob->dnssec; } (void)host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - d_request, d_require, &canonical_name, NULL); + dnssec_domains, NULL, NULL); } } } @@ -1780,8 +2110,10 @@ #ifdef SUPPORT_TLS deliver_set_expansions(addr); #endif + verify_mode = is_recipient ? US"R" : US"S"; rc = do_callout(addr, host_list, &tf, callout, callout_overall, callout_connect, options, se_mailfrom, pm_mailfrom); + verify_mode = NULL; } } else @@ -1935,7 +2267,7 @@ /* If we have carried on to verify a child address, we want the value of $address_data to be that of the child */ - vaddr->p.address_data = addr->p.address_data; + vaddr->prop.address_data = addr->prop.address_data; yield = OK; goto out; } @@ -1967,8 +2299,8 @@ fprintf(f, "%s", CS addr->address); #ifdef EXPERIMENTAL_SRS - if(addr->p.srs_sender) - fprintf(f, " [srs = %s]", addr->p.srs_sender); + if(addr->prop.srs_sender) + fprintf(f, " [srs = %s]", addr->prop.srs_sender); #endif /* If the address is a duplicate, show something about it. */ @@ -2031,6 +2363,12 @@ while (len++ < maxaddlen) fprintf(f," "); if (h->mx >= 0) fprintf(f, "MX=%d", h->mx); if (h->port != PORT_NONE) fprintf(f, " port=%d", h->port); + if (running_in_test_harness) +#ifndef DISABLE_DNSSEC + fprintf(f, " ad=%s", h->dnssec==DS_YES ? "yes" : "no"); +#else + fprintf(f, " ad=no"); +#endif if (h->status == hstatus_unusable) fprintf(f, " ** unusable **"); fprintf(f, "\n"); } @@ -2150,7 +2488,8 @@ verb = US"begins"; } - *msgptr = string_printing( + /* deconst cast ok as we're passing a non-const to string_printing() */ + *msgptr = US string_printing( string_sprintf("%s: failing address in \"%.*s:\" header %s: %.*s", errmess, tt - h->text, h->text, verb, len, s)); @@ -2671,9 +3010,9 @@ /* The rest of the line is the data we want. We turn it into printing characters when we save it, so that it cannot mess up the format of any logging or Received: lines into which it gets inserted. We keep a maximum of 127 -characters. */ +characters. The deconst cast is ok as we fed a nonconst to string_printing() */ -sender_ident = string_printing(string_copyn(p, 127)); +sender_ident = US string_printing(string_copyn(p, 127)); DEBUG(D_ident) debug_printf("sender_ident = %s\n", sender_ident); END_OFF: @@ -2718,7 +3057,7 @@ */ int -check_host(void *arg, uschar *ss, uschar **valueptr, uschar **error) +check_host(void *arg, const uschar *ss, const uschar **valueptr, uschar **error) { check_host_block *cb = (check_host_block *)arg; int mlen = -1; @@ -2726,7 +3065,7 @@ BOOL iplookup = FALSE; BOOL isquery = FALSE; BOOL isiponly = cb->host_name != NULL && cb->host_name[0] == 0; -uschar *t; +const uschar *t; uschar *semicolon; uschar **aliases; @@ -2907,6 +3246,10 @@ h.address = NULL; h.mx = MX_NONE; + /* Using byname rather than bydns here means we cannot determine dnssec + status. On the other hand it is unclear how that could be either + propagated up or enforced. */ + rc = host_find_byname(&h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, FALSE); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) { @@ -2939,7 +3282,7 @@ if ((semicolon = Ustrchr(ss, ';')) != NULL) { - uschar *affix; + const uschar *affix; int partial, affixlen, starflags, id; *semicolon = 0; @@ -3040,12 +3383,12 @@ "+allow_unknown" was met earlier in the list, in which case OK is returned. */ int -verify_check_this_host(uschar **listptr, unsigned int *cache_bits, - uschar *host_name, uschar *host_address, uschar **valueptr) +verify_check_this_host(const uschar **listptr, unsigned int *cache_bits, + const uschar *host_name, const uschar *host_address, const uschar **valueptr) { int rc; unsigned int *local_cache_bits = cache_bits; -uschar *save_host_address = deliver_host_address; +const uschar *save_host_address = deliver_host_address; check_host_block cb; cb.host_name = host_name; cb.host_address = host_address; @@ -3085,6 +3428,15 @@ /************************************************* +* Check the given host item matches a list * +*************************************************/ +int +verify_check_given_host(uschar **listptr, host_item *host) +{ +return verify_check_this_host(CUSS listptr, NULL, host->name, host->address, NULL); +} + +/************************************************* * Check the remote host matches a list * *************************************************/ @@ -3103,7 +3455,7 @@ int verify_check_host(uschar **listptr) { -return verify_check_this_host(listptr, sender_host_cache, NULL, +return verify_check_this_host(CUSS listptr, sender_host_cache, NULL, (sender_host_address == NULL)? US"" : sender_host_address, NULL); } @@ -3271,13 +3623,13 @@ dns_record *rr; dns_address **addrp = &(cb->rhs); for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { if (rr->type == T_A) { dns_address *da = dns_address_from_rr(&dnsa, rr); - if (da != NULL) + if (da) { *addrp = da; while (da->next != NULL) da = da->next; @@ -3334,7 +3686,7 @@ { int ipsep = ','; uschar ip[46]; - uschar *ptr = iplist; + const uschar *ptr = iplist; uschar *res; /* Handle exact matching */ @@ -3537,11 +3889,11 @@ */ int -verify_check_dnsbl(uschar **listptr) +verify_check_dnsbl(const uschar **listptr) { int sep = 0; int defer_return = FAIL; -uschar *list = *listptr; +const uschar *list = *listptr; uschar *domain; uschar *s; uschar buffer[1024]; @@ -3687,7 +4039,7 @@ uschar keybuffer[256]; uschar keyrevadd[128]; - while ((keydomain = string_nextinlist(&key, &keysep, keybuffer, + while ((keydomain = string_nextinlist(CUSS &key, &keysep, keybuffer, sizeof(keybuffer))) != NULL) { uschar *prepend = keydomain; diff -Nru exim4-4.84/src/version.sh exim4-4.86~RC4/src/version.sh --- exim4-4.84/src/version.sh 2014-08-11 15:12:36.000000000 +0200 +++ exim4-4.86~RC4/src/version.sh 2015-06-28 04:46:23.000000000 +0200 @@ -1,4 +1,4 @@ # automatically generated file - see ../scripts/reversion -EXIM_RELEASE_VERSION="4.84" -EXIM_VARIANT_VERSION="" +EXIM_RELEASE_VERSION="4.86" +EXIM_VARIANT_VERSION="_RC4" EXIM_COMPILE_NUMBER="1"