From: Wietse Venema Date: Sat, 23 May 2015 05:00:00 +0000 (-0500) Subject: postfix-3.1-20150523 X-Git-Tag: v3.1.0-RC1~22 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=469db3d275fb467932ca0bd72e58ecd59f0d4715;p=thirdparty%2Fpostfix.git postfix-3.1-20150523 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 8271d81c8..8ac693bcd 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -21727,3 +21727,33 @@ Apologies for any names omitted. Bugfix (introduced: 19970309): reset errno before calling readdir(), in order to distinguish between end-of-directory and an error condition. File: scandir,c, + +20150426 + + Cleanup: when transmitting an attribute-value sequence + between Postfix processes, a hash table may now appear at + any position instead of only at the end. Files: + util/attr_scan{0,64,plain}.c, util/attr_print{0,64,plain}.c, + util/attr_scan{0,64,plain}.ref. + + Feature: milter_macro_defaults, an optional list of macro + name=value pairs that specify default values for Milter + macros. When a macro is to be sent to a Milter application, + Postfix will send its default value when no value is available + from the mail delivery context. For example, with + "milter_macro_defaults = auth_type=TLS", Postfix will send + an auth_type of "TLS" unless a remote client authenticates + with SASL. Files: mantools/postlink, proto/MILTER_README.html, + proto/postconf.proto, cleanup/cleanup.c, cleanup/cleanup_init.c, + cleanup/cleanup_milter.c, global/mail_params.h, milter/milter.c, + milter/milter.h, smtpd/smtpd.c, smtpd/smtpd_milter.c. + +20150501 + + Support for Linux 4.*, and some simplification for future + makedefs files. Files: makedefs, util/sys_defs.h. + +20150502 + + Cleanup: updated the examples in MILTER_README. File: + proto/MILTER_README.html diff --git a/postfix/README_FILES/MILTER_README b/postfix/README_FILES/MILTER_README index e816dba89..730aa5b00 100644 --- a/postfix/README_FILES/MILTER_README +++ b/postfix/README_FILES/MILTER_README @@ -12,11 +12,9 @@ is queued. The reason for adding Milter support to Postfix is that there exists a large collection of applications, not only to block unwanted mail, but also to verify -authenticity (examples: OpenDKIM, DomainKeys Identified Mail (DKIM), -SenderID+SPF and DomainKeys) or to digitally sign mail (examples: OpenDKIM, -DomainKeys Identified Mail (DKIM), DomainKeys). Having yet another Postfix- -specific version of all that software is a poor use of human and system -resources. +authenticity (examples: OpenDKIM and DMARC) or to digitally sign mail (example: +OpenDKIM). Having yet another Postfix-specific version of all that software is +a poor use of human and system resources. The Milter protocol has evolved over time, and different Postfix versions implement different feature sets. See the workarounds and limitations sections @@ -89,51 +87,26 @@ deals with C applications only. For these, you need an object library that implements the Sendmail 8 Milter protocol. Postfix currently does not provide such a library, but Sendmail does. - * The first option is to use a pre-compiled library. Some systems install the - Sendmail libmilter library by default. With other systems, libmilter may be - provided by a package (called "sendmail-devel" on some Linux systems). +Some systems install the Sendmail libmilter library by default. With other +systems, libmilter may be provided by a package (called "sendmail-devel" on +some Linux systems). - Once libmilter is installed, applications such as OpenDKIM, dkim-milter and - sid-milter build out of the box without requiring any tinkering: +Once libmilter is installed, applications such as OpenDKIM and OpenDMARC build +out of the box without requiring any tinkering: - $ ggzzccaatt ooppeennddkkiimm--xx..yy..zz..ttaarr..ggzz || ttaarr xxff -- - $ ccdd ooppeennddkkiimm--xx..yy..zz - $ ..//ccoonnffiigguurree ......ooppttiioonnss...... - $ mmaakkee - [...lots of output omitted...] - $ mmaakkee iinnssttaallll - - $ ggzzccaatt ddkkiimm--mmiilltteerr--xx..yy..zz..ttaarr..ggzz || ttaarr xxff -- - $ ccdd ddkkiimm--mmiilltteerr--xx..yy..zz - $ mmaakkee - [...lots of output omitted...] - - * The other option is to build the libmilter library from Sendmail source - code: - - $ ggzzccaatt sseennddmmaaiill--xx..yy..zz..ttaarr..ggzz || ttaarr xxff -- - $ ccdd sseennddmmaaiill--xx..yy..zz//lliibbmmiilltteerr - $ mmaakkee - [...lots of output omitted...] - - After building your own libmilter library, follow the installation - instructions in the Milter application source distribution to specify the - location of the libmilter include files and object library. Typically, - these settings are configured in a file named sid-filter/Makefile.m4 or - similar: - - APPENDDEF(`confINCDIRS', `-I/some/where/sendmail-x.y.z/include') - APPENDDEF(`confLIBDIRS', `-L/some/where/sendmail-x.y.z/obj.systemtype/ - libmilter') - - Then build the Milter application. + $ ggzzccaatt ooppeennddkkiimm--xx..yy..zz..ttaarr..ggzz || ttaarr xxff -- + $ ccdd ooppeennddkkiimm--xx..yy..zz + $ ..//ccoonnffiigguurree ......ooppttiioonnss...... + $ mmaakkee + [...lots of output omitted...] + $ mmaakkee iinnssttaallll RRuunnnniinngg MMiilltteerr aapppplliiccaattiioonnss To run a Milter application, see the documentation of the filter for options. A typical command looks like this: - # //ssoommee//wwhheerree//ddkkiimm--ffiilltteerr --uu uusseerriidd --pp iinneett::ppoorrttnnuummbbeerr@@llooccaallhhoosstt ......ootthheerr + # //ssoommee//wwhheerree//ooppeennddkkiimm --ll --uu uusseerriidd --pp iinneett::ppoorrttnnuummbbeerr@@llooccaallhhoosstt ......ootthheerr ooppttiioonnss...... Please specify a userid value that isn't used for other applications (not @@ -155,6 +128,7 @@ Information in this section: * Milter protocol timeouts * Different settings for different Milter applications * Sendmail macro emulation + * What macros will Postfix send to Milters? SSMMTTPP--OOnnllyy MMiilltteerr aapppplliiccaattiioonnss @@ -164,10 +138,11 @@ mail from authorized SMTP clients. Mail that arrives via the Postfix smtpd(8) server is not filtered by the non-SMTP filters that are described in the next section. -NOTE: Do not use the header_checks(5) IGNORE action to remove Postfix's own -Received: message header. This causes problems with mail signing filters. -Instead, keep Postfix's own Received: message header and use the header_checks -(5) REPLACE action to sanitize information. + NOTE for Postfix versions that have a mail_release_date before 20141018: do + not use the header_checks(5) IGNORE action to remove Postfix's own + Received: message header. This causes problems with mail signing filters. + Instead, keep Postfix's own Received: message header and use the + header_checks(5) REPLACE action to sanitize information. You specify SMTP-only Milter applications (there can be more than one) with the smtpd_milters parameter. Each Milter application is identified by the name of @@ -365,7 +340,9 @@ Instead of a server endpoint, we now have a list enclosed in {}. * Line 3: The remainder of the list contains per-Milter settings. These settings override global main.cf parameters, and have the same name as - those parameters, without the "milter_" prefix. + those parameters, without the "milter_" prefix. The per-Milter settings + that are supported as of Postfix 3.0 are command_timeout, connect_timeout, + content_timeout, default_action, and protocol. Inside the list, syntax is similar to what we already know from main.cf: items separated by space or comma. There is one difference: yyoouu mmuusstt eenncclloossee aa @@ -462,10 +439,12 @@ Sendmail. See the workarounds section below for solutions. |v |Always |value of milter_macro_v | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | +WWhhaatt mmaaccrrooss wwiillll PPoossttffiixx sseenndd ttoo MMiilltteerrss?? + Postfix sends specific sets of macros at different Milter protocol stages. The -sets are configured with the parameters as described in the table (EOH = end of -headers; EOM = end of message). The protocol version is a number that Postfix -sends at the beginning of the Milter protocol handshake. +sets are configured with the parameters as shown in the table below (EOH = end +of headers; EOM = end of message). The protocol version is a number that +Postfix sends at the beginning of the Milter protocol handshake. As of Sendmail 8.14.0, Milter applications can specify what macros they want to receive at different Milter protocol stages. An application-specified list @@ -492,6 +471,16 @@ takes precedence over a Postfix-specified list. |milter_unknown_command_macros|3 or higher |unknown command | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | +By default, Postfix will send only macros whose values have been updated with +information from main.cf or master.cf, from an SMTP session (for example; SASL +login, or TLS certificates) or from a Mail delivery transaction (for example; +queue ID, sender, or recipient). + +To force a macro to be sent even when its value has not been updated, you may +specify macro default values with the milter_macro_defaults parameter. Specify +zero or more name=value pairs separated by comma or whitespace; you may even +specify macro names that Postfix does know about! + WWoorrkkaarroouunnddss * To avoid breaking DKIM etc. signatures with an SMTP-based content filter, diff --git a/postfix/WISHLIST b/postfix/WISHLIST index e347e2d3d..aa4d85243 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -10,10 +10,22 @@ Wish list: Things to do after the stable release: + TLS certificate provenance: indicate whether a subject + name/issuer are verified or not (for example, change the + attribute name to unverified_ccert_subject etc.). This is + relevant only for fingerprint-based authentication including + DANE, and affects logging, SMTPD policy, and Milters. + + Exploit GCC 3.4+ __attribute__((warn_unused_result)) to + warn about unused function result values. + Generalize the daemon '-S' stand-alone mode, so that it can be used with custom configuration files for request/reply regression testing. + Update the list of Sendmail macros that Postfix can send + to Milters (auth_ssf and TLS-related). + replace str*casecmp() calls with _utf8() equivalents for trivial-rewrite lookups. diff --git a/postfix/html/MILTER_README.html b/postfix/html/MILTER_README.html index b6b6f6478..7494e592b 100644 --- a/postfix/html/MILTER_README.html +++ b/postfix/html/MILTER_README.html @@ -28,16 +28,10 @@ SMTP commands (HELO, MAIL FROM, etc.) as well as mail content

The reason for adding Milter support to Postfix is that there exists a large collection of applications, not only to block unwanted mail, but also to verify authenticity (examples: OpenDKIM, DomainKeys -Identified Mail (DKIM), SenderID+SPF and -DomainKeys) -or to digitally sign mail (examples: OpenDKIM, DomainKeys -Identified Mail (DKIM), DomainKeys). +href="http://www.opendkim.org/">OpenDKIM and DMARC ) +or to digitally sign mail (example: OpenDKIM). Having yet another Postfix-specific version of all that software is a poor use of human and system resources.

@@ -206,17 +200,14 @@ an object library that implements the Sendmail 8 Milter protocol. Postfix currently does not provide such a library, but Sendmail does.

- -

Running Milter applications

To run a Milter application, see the documentation of the filter @@ -275,7 +228,7 @@ for options. A typical command looks like this:

-# /some/where/dkim-filter -u userid -p inet:portnumber@localhost ...other options...
+# /some/where/opendkim -l -u userid -p inet:portnumber@localhost ...other options...
 
@@ -308,6 +261,8 @@ applications
  • Sendmail macro emulation +
  • What macros will Postfix send to Milters? +

    SMTP-Only Milter applications

    @@ -318,11 +273,12 @@ unwanted mail, and to sign mail from authorized SMTP clients. Mail that arrives via the Postfix smtpd(8) server is not filtered by the non-SMTP filters that are described in the next section.

    -

    NOTE: Do not use the header_checks(5) IGNORE action to remove +

    NOTE for Postfix versions that have a mail_release_date +before 20141018: do not use the header_checks(5) IGNORE action to remove Postfix's own Received: message header. This causes problems with mail signing filters. Instead, keep Postfix's own Received: message header and use the header_checks(5) REPLACE action to sanitize -information.

    +information.

    You specify SMTP-only Milter applications (there can be more than one) with the smtpd_milters parameter. Each Milter application @@ -596,7 +552,9 @@ earlier.

  • Line 3: The remainder of the list contains per-Milter settings. These settings override global main.cf parameters, and have the same name as those parameters, without the "milter_" prefix. -

    +The per-Milter settings that are supported as of Postfix 3.0 are +command_timeout, connect_timeout, content_timeout, default_action, +and protocol.

    @@ -706,9 +664,11 @@ With rejected recipient: "error" +

    What macros will Postfix send to Milters?

    +

    Postfix sends specific sets of macros at different Milter protocol -stages. The sets are configured with the parameters as described -in the table (EOH = end of headers; EOM = end of message). The +stages. The sets are configured with the parameters as shown in the +table below (EOH = end of headers; EOM = end of message). The protocol version is a number that Postfix sends at the beginning of the Milter protocol handshake.

    @@ -752,6 +712,17 @@ TO +

    By default, Postfix will send only macros whose values have been +updated with information from main.cf or master.cf, from an SMTP session +(for example; SASL login, or TLS certificates) or from a Mail delivery +transaction (for example; queue ID, sender, or recipient).

    + +

    To force a macro to be sent even when its value has not been updated, +you may specify macro default values with the milter_macro_defaults +parameter. Specify zero or more name=value pairs separated by +comma or whitespace; you may even specify macro names that Postfix does +know about!

    +

    Workarounds

    SMTP-Only Milter applications

    @@ -318,11 +273,12 @@ unwanted mail, and to sign mail from authorized SMTP clients. Mail that arrives via the Postfix smtpd(8) server is not filtered by the non-SMTP filters that are described in the next section.

    -

    NOTE: Do not use the header_checks(5) IGNORE action to remove +

    NOTE for Postfix versions that have a mail_release_date +before 20141018: do not use the header_checks(5) IGNORE action to remove Postfix's own Received: message header. This causes problems with mail signing filters. Instead, keep Postfix's own Received: message header and use the header_checks(5) REPLACE action to sanitize -information.

    +information.

    You specify SMTP-only Milter applications (there can be more than one) with the smtpd_milters parameter. Each Milter application @@ -596,7 +552,9 @@ earlier.

  • Line 3: The remainder of the list contains per-Milter settings. These settings override global main.cf parameters, and have the same name as those parameters, without the "milter_" prefix. -

    +The per-Milter settings that are supported as of Postfix 3.0 are +command_timeout, connect_timeout, content_timeout, default_action, +and protocol.

    @@ -706,9 +664,11 @@ With rejected recipient: "error" +

    What macros will Postfix send to Milters?

    +

    Postfix sends specific sets of macros at different Milter protocol -stages. The sets are configured with the parameters as described -in the table (EOH = end of headers; EOM = end of message). The +stages. The sets are configured with the parameters as shown in the +table below (EOH = end of headers; EOM = end of message). The protocol version is a number that Postfix sends at the beginning of the Milter protocol handshake.

    @@ -752,6 +712,17 @@ TO +

    By default, Postfix will send only macros whose values have been +updated with information from main.cf or master.cf, from an SMTP session +(for example; SASL login, or TLS certificates) or from a Mail delivery +transaction (for example; queue ID, sender, or recipient).

    + +

    To force a macro to be sent even when its value has not been updated, +you may specify macro default values with the milter_macro_defaults +parameter. Specify zero or more name=value pairs separated by +comma or whitespace; you may even specify macro names that Postfix does +know about!

    +

    Workarounds

      diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index edb85cf2d..d710b6d9a 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -11678,6 +11678,20 @@ meanings.

      This feature is available in Postfix 2.3 and later.

      +%PARAM milter_macro_defaults + +

      Optional list of name=value pairs that specify default +values for arbitrary macros that Postfix may send to Milter +applications. These defaults are used when there is no corresponding +information from the message delivery context.

      + +

      Specify name=value or {name}=value pairs separated +by comma or whitespace. Enclose a pair in "{}" when a value contains +comma or whitespace (this form ignores whitespace after the enclosing +"{", around the "=", and before the enclosing "}").

      + +

      This feature is available in Postfix 3.1 and later.

      + %PARAM milter_macro_v $mail_name $mail_version

      The {v} macro value for Milter (mail filter) applications. diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c index c48a41047..e81dfab6e 100644 --- a/postfix/src/cleanup/cleanup.c +++ b/postfix/src/cleanup/cleanup.c @@ -189,6 +189,12 @@ /* .IP "\fBmilter_header_checks (empty)\fR" /* Optional lookup tables for content inspection of message headers /* that are produced by Milter applications. +/* .PP +/* Available in Postfix version 3.1 and later: +/* .IP "\fBmilter_macro_defaults (empty)\fR" +/* Optional list of \fIname=value\fR pairs that specify default +/* values for arbitrary macros that Postfix may send to Milter +/* applications. /* MIME PROCESSING CONTROLS /* .ad /* .fi diff --git a/postfix/src/cleanup/cleanup_init.c b/postfix/src/cleanup/cleanup_init.c index 37ec5b0f7..5bae3587b 100644 --- a/postfix/src/cleanup/cleanup_init.c +++ b/postfix/src/cleanup/cleanup_init.c @@ -162,6 +162,7 @@ char *var_milt_eod_macros; /* end-of-data macros */ char *var_milt_unk_macros; /* unknown command macros */ char *var_cleanup_milters; /* non-SMTP mail */ char *var_milt_head_checks; /* post-Milter header checks */ +char *var_milt_macro_deflts; /* default macro settings */ int var_auto_8bit_enc_hdr; /* auto-detect 8bit encoding header */ int var_always_add_hdrs; /* always add missing headers */ int var_virt_addrlen_limit; /* stop exponential growth */ @@ -231,6 +232,7 @@ const CONFIG_STR_TABLE cleanup_str_table[] = { VAR_MILT_UNK_MACROS, DEF_MILT_UNK_MACROS, &var_milt_unk_macros, 0, 0, VAR_CLEANUP_MILTERS, DEF_CLEANUP_MILTERS, &var_cleanup_milters, 0, 0, VAR_MILT_HEAD_CHECKS, DEF_MILT_HEAD_CHECKS, &var_milt_head_checks, 0, 0, + VAR_MILT_MACRO_DEFLTS, DEF_MILT_MACRO_DEFLTS, &var_milt_macro_deflts, 0, 0, 0, }; @@ -410,7 +412,8 @@ void cleanup_pre_jail(char *unused_name, char **unused_argv) var_milt_data_macros, var_milt_eoh_macros, var_milt_eod_macros, - var_milt_unk_macros); + var_milt_unk_macros, + var_milt_macro_deflts); flush_init(); } diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c index cc60f3595..fb371222e 100644 --- a/postfix/src/cleanup/cleanup_milter.c +++ b/postfix/src/cleanup/cleanup_milter.c @@ -1801,14 +1801,6 @@ static const char *cleanup_milter_eval(const char *name, void *ptr) * that we forward all Sendmail macros via XFORWARD. */ - /* - * Canonicalize the name. - */ - if (*name != '{') { /* } */ - vstring_sprintf(state->temp1, "{%s}", name); - name = STR(state->temp1); - } - /* * System macros. */ diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 54a70e25f..823fc0c85 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -3300,6 +3300,10 @@ extern char *var_milt_v; #define DEF_MILT_HEAD_CHECKS "" extern char *var_milt_head_checks; +#define VAR_MILT_MACRO_DEFLTS "milter_macro_defaults" +#define DEF_MILT_MACRO_DEFLTS "" +extern char *var_milt_macro_deflts; + /* * What internal mail do we inspect/stamp/etc.? This is not yet safe enough * to enable world-wide. diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 0ed0e08c5..b663dfcda 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20150421" +#define MAIL_RELEASE_DATE "20150523" #define MAIL_VERSION_NUMBER "3.1" #ifdef SNAPSHOT diff --git a/postfix/src/milter/milter.c b/postfix/src/milter/milter.c index c889d98b8..969feef35 100644 --- a/postfix/src/milter/milter.c +++ b/postfix/src/milter/milter.c @@ -11,7 +11,8 @@ /* conn_macros, helo_macros, /* mail_macros, rcpt_macros, /* data_macros, eoh_macros, -/* eod_macros, unk_macros) +/* eod_macros, unk_macros, +/* macro_deflts) /* const char *milter_names; /* int conn_timeout; /* int cmd_timeout; @@ -26,6 +27,7 @@ /* const char *eoh_macros; /* const char *eod_macros; /* const char *unk_macros; +/* const char *macro_deflts; /* /* void milter_free(milters) /* MILTERS *milters; @@ -119,7 +121,9 @@ /* milter_create() instantiates the milter clients specified /* with the milter_names argument. The conn_macros etc. /* arguments specify the names of macros that are sent to the -/* mail filter applications upon a connect etc. event. This +/* mail filter applications upon a connect etc. event, and the +/* macro_deflts argument specifies macro defaults that will be used +/* only if the application's lookup call-back returns null. This /* function should be called during process initialization, /* before entering a chroot jail. The timeout parameters specify /* time limits for the completion of the specified request @@ -239,6 +243,7 @@ #include #include #include +#include /* Global library. */ @@ -259,6 +264,59 @@ */ #define STR(x) vstring_str(x) +/* milter_macro_defaults_create - parse default macro entries */ + +HTABLE *milter_macro_defaults_create(const char *macro_defaults) +{ + const char myname[] = "milter_macro_defaults_create"; + char *saved_defaults = mystrdup(macro_defaults); + char *cp = saved_defaults; + HTABLE *table = 0; + VSTRING *canon_buf = 0; + char *nameval; + + while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) { + const char *err; + char *name; + char *value; + + /* + * Split the input into (name, value) pairs. Allow the forms + * name=value and { name = value }, where the last form ignores + * whitespace after the opening "{", around the "=", and before the + * closing "}". A name may also be specified as {name}. + * + * Use the form {name} for table lookups, because that is the form of + * the S8_MAC_* macro names. + */ + if (*nameval == CHARS_BRACE[0] + && nameval[balpar(nameval, CHARS_BRACE)] != '=' + && (err = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) + msg_fatal("malformed default macro entry: %s in \"%s\"", + err, macro_defaults); + if ((err = split_nameval(nameval, &name, &value)) != 0) + msg_fatal("malformed default macro entry: %s in \"%s\"", + err, macro_defaults); + if (*name != '{') /* } */ + name = STR(vstring_sprintf(canon_buf ? canon_buf : + (canon_buf = vstring_alloc(20)), "{%s}", name)); + if (table == 0) + table = htable_create(1); + if (htable_find(table, name) != 0) { + msg_warn("ignoring multiple default macro entries for %s in \"%s\"", + name, macro_defaults); + } else { + (void) htable_enter(table, name, mystrdup(value)); + if (msg_verbose) + msg_info("%s: add name=%s default=%s", myname, name, value); + } + } + myfree(saved_defaults); + if (canon_buf) + vstring_free(canon_buf); + return (table); +} + /* milter_macro_lookup - look up macros */ static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names) @@ -267,19 +325,28 @@ static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names) char *saved_names = mystrdup(macro_names); char *cp = saved_names; ARGV *argv = argv_alloc(10); + VSTRING *canon_buf = vstring_alloc(20); const char *value; const char *name; while ((name = mystrtok(&cp, CHARS_COMMA_SP)) != 0) { if (msg_verbose) msg_info("%s: \"%s\"", myname, name); + if (*name != '{') /* } */ + name = STR(vstring_sprintf(canon_buf, "{%s}", name)); if ((value = milters->mac_lookup(name, milters->mac_context)) != 0) { if (msg_verbose) msg_info("%s: result \"%s\"", myname, value); argv_add(argv, name, value, (char *) 0); + } else if (milters->macro_defaults != 0 + && (value = htable_find(milters->macro_defaults, name)) != 0) { + if (msg_verbose) + msg_info("%s: using default \"%s\"", myname, value); + argv_add(argv, name, value, (char *) 0); } } myfree(saved_names); + vstring_free(canon_buf); return (argv); } @@ -572,7 +639,8 @@ MILTERS *milter_new(const char *names, int msg_timeout, const char *protocol, const char *def_action, - MILTER_MACROS *macros) + MILTER_MACROS *macros, + HTABLE *macro_defaults) { MILTERS *milters; MILTER *head = 0; @@ -642,6 +710,7 @@ MILTERS *milter_new(const char *names, milters->mac_lookup = 0; milters->mac_context = 0; milters->macros = macros; + milters->macro_defaults = macro_defaults; milters->add_header = 0; milters->upd_header = milters->ins_header = 0; milters->del_header = 0; @@ -664,6 +733,8 @@ void milter_free(MILTERS *milters) next = m->next, m->free(m); if (milters->macros) milter_macros_free(milters->macros); + if (milters->macro_defaults) + htable_free(milters->macro_defaults, myfree); myfree((void *) milters); } @@ -720,6 +791,18 @@ int milter_send(MILTERS *milters, VSTREAM *stream) (void *) milters->macros), ATTR_TYPE_END); + /* + * Send the filter macro defaults. + */ + count = milters->macro_defaults ? milters->macro_defaults->used : 0; + (void) attr_print(stream, ATTR_FLAG_MORE, + SEND_ATTR_INT(MAIL_ATTR_SIZE, count), + ATTR_TYPE_END); + if (count > 0) + (void) attr_print(stream, ATTR_FLAG_MORE, + SEND_ATTR_HASH(milters->macro_defaults), + ATTR_TYPE_END); + /* * Send the filter instances. */ @@ -749,6 +832,7 @@ MILTERS *milter_receive(VSTREAM *stream, int count) MILTER *head = 0; MILTER *tail = 0; MILTER *milter = 0; + int macro_default_count; if (msg_verbose) msg_info("receive %d milters", count); @@ -763,9 +847,10 @@ MILTERS *milter_receive(VSTREAM *stream, int count) #define NO_PROTOCOL ((char *) 0) #define NO_ACTION ((char *) 0) #define NO_MACROS ((MILTER_MACROS *) 0) +#define NO_MACRO_DEFLTS ((HTABLE *) 0) milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION, - NO_MACROS); + NO_MACROS, NO_MACRO_DEFLTS); /* * XXX Optimization: don't send or receive further information when there @@ -786,6 +871,21 @@ MILTERS *milter_receive(VSTREAM *stream, int count) return (0); } + /* + * Receive the filter macro defaults. + */ + if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, + RECV_ATTR_INT(MAIL_ATTR_SIZE, ¯o_default_count), + ATTR_TYPE_END) != 1 + || (macro_default_count > 0 + && attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, + RECV_ATTR_HASH(milters->macro_defaults + = htable_create(1)), + ATTR_TYPE_END) != macro_default_count)) { + milter_free(milters); + return (0); + } + /* * Receive the filters. */ @@ -872,6 +972,7 @@ int main(int argc, char **argv) MILTERS *milters = 0; char *conn_macros, *helo_macros, *mail_macros, *rcpt_macros; char *data_macros, *eoh_macros, *eod_macros, *unk_macros; + char *macro_deflts; VSTRING *inbuf = vstring_alloc(100); char *bufp; char *cmd; @@ -879,7 +980,7 @@ int main(int argc, char **argv) int istty = isatty(vstream_fileno(VSTREAM_IN)); conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros - = eoh_macros = eod_macros = unk_macros = ""; + = eoh_macros = eod_macros = unk_macros = macro_deflts = ""; msg_vstream_init(argv[0], VSTREAM_ERR); while ((ch = GETOPT(argc, argv, "V:v")) > 0) { @@ -934,7 +1035,7 @@ int main(int argc, char **argv) var_milt_protocol, var_milt_def_action, conn_macros, helo_macros, mail_macros, rcpt_macros, data_macros, eoh_macros, - eod_macros, unk_macros); + eod_macros, unk_macros, macro_deflts); } else if (strcmp(cmd, "free") == 0 && argv->argc == 0) { if (milters == 0) { msg_warn("no milters"); diff --git a/postfix/src/milter/milter.h b/postfix/src/milter/milter.h index bf25fccc0..9ef5d35cb 100644 --- a/postfix/src/milter/milter.h +++ b/postfix/src/milter/milter.h @@ -84,6 +84,11 @@ extern int milter_macros_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *); #define MILTER_MACROS_ALLOC_ZERO 1 /* null pointer */ #define MILTER_MACROS_ALLOC_EMPTY 2 /* mystrdup(""); */ + /* + * Helper to parse list of name=value default macro settings. + */ +extern struct HTABLE *milter_macro_defaults_create(const char *); + /* * A bunch of Milters. */ @@ -101,6 +106,7 @@ typedef struct MILTERS { MILTER_MAC_LOOKUP_FN mac_lookup; void *mac_context; /* macro lookup context */ struct MILTER_MACROS *macros; + struct HTABLE *macro_defaults; void *chg_context; /* context for queue file changes */ MILTER_ADD_HEADER_FN add_header; MILTER_EDIT_HEADER_FN upd_header; @@ -116,14 +122,16 @@ typedef struct MILTERS { #define milter_create(milter_names, conn_timeout, cmd_timeout, msg_timeout, \ protocol, def_action, conn_macros, helo_macros, \ mail_macros, rcpt_macros, data_macros, eoh_macros, \ - eod_macros, unk_macros) \ + eod_macros, unk_macros, macro_deflts) \ milter_new(milter_names, conn_timeout, cmd_timeout, msg_timeout, \ protocol, def_action, milter_macros_create(conn_macros, \ helo_macros, mail_macros, rcpt_macros, data_macros, \ - eoh_macros, eod_macros, unk_macros)) + eoh_macros, eod_macros, unk_macros), \ + milter_macro_defaults_create(macro_deflts)) extern MILTERS *milter_new(const char *, int, int, int, const char *, - const char *, MILTER_MACROS *); + const char *, MILTER_MACROS *, + struct HTABLE *); extern void milter_macro_callback(MILTERS *, MILTER_MAC_LOOKUP_FN, void *); extern void milter_edit_callback(MILTERS *milters, MILTER_ADD_HEADER_FN, MILTER_EDIT_HEADER_FN, MILTER_EDIT_HEADER_FN, diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 4208795fb..a8d55cc5a 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -251,6 +251,12 @@ /* .IP "\fBmilter_end_of_data_macros (see 'postconf -d' output)\fR" /* The macros that are sent to Milter (mail filter) applications /* after the message end-of-data. +/* .PP +/* Available in Postfix version 3.1 and later: +/* .IP "\fBmilter_macro_defaults (empty)\fR" +/* Optional list of \fIname=value\fR pairs that specify default +/* values for arbitrary macros that Postfix may send to Milter +/* applications. /* GENERAL CONTENT INSPECTION CONTROLS /* .ad /* .fi @@ -1343,6 +1349,7 @@ char *var_milt_data_macros; char *var_milt_eoh_macros; char *var_milt_eod_macros; char *var_milt_unk_macros; +char *var_milt_macro_deflts; bool var_smtpd_client_port_log; char *var_stress; @@ -5538,7 +5545,8 @@ static void post_jail_init(char *unused_name, char **unused_argv) var_milt_data_macros, var_milt_eoh_macros, var_milt_eod_macros, - var_milt_unk_macros); + var_milt_unk_macros, + var_milt_macro_deflts); else smtpd_input_transp_mask |= INPUT_TRANSP_MILTER; } @@ -5750,6 +5758,7 @@ int main(int argc, char **argv) VAR_MILT_DEF_ACTION, DEF_MILT_DEF_ACTION, &var_milt_def_action, 1, 0, VAR_MILT_DAEMON_NAME, DEF_MILT_DAEMON_NAME, &var_milt_daemon_name, 1, 0, VAR_MILT_V, DEF_MILT_V, &var_milt_v, 1, 0, + VAR_MILT_MACRO_DEFLTS, DEF_MILT_MACRO_DEFLTS, &var_milt_macro_deflts, 0, 0, VAR_STRESS, DEF_STRESS, &var_stress, 0, 0, VAR_UNV_FROM_WHY, DEF_UNV_FROM_WHY, &var_unv_from_why, 0, 0, VAR_UNV_RCPT_WHY, DEF_UNV_RCPT_WHY, &var_unv_rcpt_why, 0, 0, diff --git a/postfix/src/smtpd/smtpd_milter.c b/postfix/src/smtpd/smtpd_milter.c index 833148a7e..bd0fb2c13 100644 --- a/postfix/src/smtpd/smtpd_milter.c +++ b/postfix/src/smtpd/smtpd_milter.c @@ -71,14 +71,6 @@ const char *smtpd_milter_eval(const char *name, void *ptr) if (state->expand_buf == 0) state->expand_buf = vstring_alloc(10); - /* - * Canonicalize the name. - */ - if (*name != '{') { /* } */ - vstring_sprintf(state->expand_buf, "{%s}", name); - name = STR(state->expand_buf); - } - /* * System macros. */ @@ -143,7 +135,7 @@ const char *smtpd_milter_eval(const char *name, void *ptr) /* * MAIL FROM macros. */ -#define IF_SASL_ENABLED(s) (smtpd_sasl_is_active(state) && (s) ? (s) : 0) +#define IF_SASL_ENABLED(s) ((s) ? (s) : 0) if (strcmp(name, S8_MAC_I) == 0) return (state->queue_id); diff --git a/postfix/src/util/attr.h b/postfix/src/util/attr.h index cb9b64991..a08e6774f 100644 --- a/postfix/src/util/attr.h +++ b/postfix/src/util/attr.h @@ -38,6 +38,14 @@ #define ATTR_TYPE_DATA 5 /* Binary data */ #define ATTR_TYPE_FUNC 6 /* Function pointer */ + /* + * Optional sender-specified grouping for hash or nameval tables. + */ +#define ATTR_TYPE_OPEN '{' +#define ATTR_TYPE_CLOSE '}' +#define ATTR_NAME_OPEN "{" +#define ATTR_NAME_CLOSE "}" + #define ATTR_HASH_LIMIT 1024 /* Size of hash table */ /* diff --git a/postfix/src/util/attr_print0.c b/postfix/src/util/attr_print0.c index 44238f5c7..a916bca7b 100644 --- a/postfix/src/util/attr_print0.c +++ b/postfix/src/util/attr_print0.c @@ -174,6 +174,7 @@ int attr_vprint0(VSTREAM *fp, int flags, va_list ap) print_fn(attr_print0, fp, flags | ATTR_FLAG_MORE, print_arg); break; case ATTR_TYPE_HASH: + vstream_fwrite(fp, ATTR_NAME_OPEN, sizeof(ATTR_NAME_OPEN)); ht_info_list = htable_list(va_arg(ap, HTABLE *)); for (ht = ht_info_list; *ht; ht++) { vstream_fwrite(fp, ht[0]->key, strlen(ht[0]->key) + 1); @@ -183,6 +184,7 @@ int attr_vprint0(VSTREAM *fp, int flags, va_list ap) ht[0]->key, (char *) ht[0]->value); } myfree((void *) ht_info_list); + vstream_fwrite(fp, ATTR_NAME_CLOSE, sizeof(ATTR_NAME_CLOSE)); break; default: msg_panic("%s: unknown type code: %d", myname, attr_type); @@ -226,6 +228,7 @@ int main(int unused_argc, char **argv) SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"), SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"), SEND_ATTR_HASH(table), + SEND_ATTR_LONG(ATTR_NAME_LONG, 4321L), ATTR_TYPE_END); attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(ATTR_NAME_INT, 4711), diff --git a/postfix/src/util/attr_print64.c b/postfix/src/util/attr_print64.c index 63871a048..fc3442ec9 100644 --- a/postfix/src/util/attr_print64.c +++ b/postfix/src/util/attr_print64.c @@ -211,6 +211,8 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) print_fn(attr_print64, fp, flags | ATTR_FLAG_MORE, print_arg); break; case ATTR_TYPE_HASH: + attr_print64_str(fp, ATTR_NAME_OPEN, sizeof(ATTR_NAME_OPEN) - 1); + VSTREAM_PUTC('\n', fp); ht_info_list = htable_list(va_arg(ap, HTABLE *)); for (ht = ht_info_list; *ht; ht++) { attr_print64_str(fp, ht[0]->key, strlen(ht[0]->key)); @@ -222,6 +224,8 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) ht[0]->key, (char *) ht[0]->value); } myfree((void *) ht_info_list); + attr_print64_str(fp, ATTR_NAME_CLOSE, sizeof(ATTR_NAME_CLOSE) - 1); + VSTREAM_PUTC('\n', fp); break; default: msg_panic("%s: unknown type code: %d", myname, attr_type); @@ -265,6 +269,7 @@ int main(int unused_argc, char **argv) SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"), SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"), SEND_ATTR_HASH(table), + SEND_ATTR_LONG(ATTR_NAME_LONG, 4321L), ATTR_TYPE_END); attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(ATTR_NAME_INT, 4711), diff --git a/postfix/src/util/attr_print_plain.c b/postfix/src/util/attr_print_plain.c index f94afb9af..12cde7235 100644 --- a/postfix/src/util/attr_print_plain.c +++ b/postfix/src/util/attr_print_plain.c @@ -169,6 +169,8 @@ int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap) print_fn(attr_print_plain, fp, flags | ATTR_FLAG_MORE, print_arg); break; case ATTR_TYPE_HASH: + vstream_fwrite(fp, ATTR_NAME_OPEN, sizeof(ATTR_NAME_OPEN)); + VSTREAM_PUTC('\n', fp); ht_info_list = htable_list(va_arg(ap, HTABLE *)); for (ht = ht_info_list; *ht; ht++) { vstream_fprintf(fp, "%s=%s\n", ht[0]->key, (char *) ht[0]->value); @@ -177,6 +179,8 @@ int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap) ht[0]->key, (char *) ht[0]->value); } myfree((void *) ht_info_list); + vstream_fwrite(fp, ATTR_NAME_CLOSE, sizeof(ATTR_NAME_CLOSE)); + VSTREAM_PUTC('\n', fp); break; default: msg_panic("%s: unknown type code: %d", myname, attr_type); @@ -220,6 +224,7 @@ int main(int unused_argc, char **argv) SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"), SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"), SEND_ATTR_HASH(table), + SEND_ATTR_LONG(ATTR_NAME_LONG, 4321L), ATTR_TYPE_END); attr_print_plain(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(ATTR_NAME_INT, 4711), diff --git a/postfix/src/util/attr_scan0.c b/postfix/src/util/attr_scan0.c index 89bbd41fe..b1f301610 100644 --- a/postfix/src/util/attr_scan0.c +++ b/postfix/src/util/attr_scan0.c @@ -29,7 +29,9 @@ /* (item1 | item2) stands for choice: /* /* .in +5 -/* attr-list :== simple-attr* null +/* attr-list :== (simple-attr | multi-attr)* null +/* .br +/* multi-attr :== "{" null simple-attr* "}" null /* .br /* simple-attr :== attr-name null attr-value null /* .br @@ -98,10 +100,7 @@ /* error. /* .IP "RECV_ATTR_HASH(HTABLE *table)" /* .IP "RECV_ATTR_NAMEVAL(NVTABLE *table)" -/* All further input attributes are processed as string attributes. -/* No specific attribute sequence is enforced. -/* All attributes up to the attribute list terminator are read, -/* but only the first instance of each attribute is stored. +/* Receive a sequence of attribute names and string values. /* There can be no more than 1024 attributes in a hash table. /* .sp /* The attribute string values are stored in the hash table under @@ -109,7 +108,15 @@ /* Values from the input stream are added to the hash table. Existing /* hash table entries are not replaced. /* .sp -/* N.B. This construct must be followed by an ATTR_TYPE_END argument. +/* Note: the SEND_ATTR_HASH or SEND_ATTR_NAMEVAL requests +/* format their payload as a multi-attr sequence (see syntax +/* above). When the receiver's input does not start with a +/* multi-attr delimiter (i.e. the sender did not request +/* SEND_ATTR_HASH or SEND_ATTR_NAMEVAL), the receiver will +/* store all attribute names and values up to the attribute +/* list terminator. In terms of code, this means that the +/* RECV_ATTR_HASH or RECV_ATTR_NAMEVAL request must be followed +/* by ATTR_TYPE_END. /* .IP ATTR_TYPE_END /* This argument terminates the requested attribute list. /* .RE @@ -294,7 +301,8 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) * from the input stream instead. This is secure only when the * resulting table is queried with known to be good attribute names. */ - if (wanted_type != ATTR_TYPE_HASH) { + if (wanted_type != ATTR_TYPE_HASH + && wanted_type != ATTR_TYPE_CLOSE) { wanted_type = va_arg(ap, int); if (wanted_type == ATTR_TYPE_END) { if ((flags & ATTR_FLAG_MORE) != 0) @@ -303,9 +311,6 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) } else if (wanted_type == ATTR_TYPE_HASH) { wanted_name = "(any attribute name or list terminator)"; hash_table = va_arg(ap, HTABLE *); - if (va_arg(ap, int) !=ATTR_TYPE_END) - msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END", - myname); } else if (wanted_type != ATTR_TYPE_FUNC) { wanted_name = va_arg(ap, char *); } @@ -340,7 +345,20 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) /* * See if the caller asks for this attribute. */ + if (wanted_type == ATTR_TYPE_HASH + && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { + wanted_type = ATTR_TYPE_CLOSE; + wanted_name = "(any attribute name or '}')"; + /* Advance in the input stream. */ + continue; + } else if (wanted_type == ATTR_TYPE_CLOSE + && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { + /* Advance in the argument list. */ + wanted_type = -1; + break; + } if (wanted_type == ATTR_TYPE_HASH + || wanted_type == ATTR_TYPE_CLOSE || (wanted_type != ATTR_TYPE_END && strcmp(wanted_name, STR(name_buf)) == 0)) break; @@ -391,6 +409,7 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) return (-1); break; case ATTR_TYPE_HASH: + case ATTR_TYPE_CLOSE: if ((ch = attr_scan0_string(fp, str_buf, "input attribute value")) < 0) return (-1); @@ -409,6 +428,9 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) mystrdup(STR(str_buf))); } break; + case -1: + conversions -= 1; + break; default: msg_panic("%s: unknown type code: %d", myname, wanted_type); } @@ -447,6 +469,7 @@ int main(int unused_argc, char **used_argv) HTABLE_INFO **ht; int int_val; long long_val; + long long_val2; int ret; msg_verbose = 1; @@ -458,6 +481,7 @@ int main(int unused_argc, char **used_argv) RECV_ATTR_STR(ATTR_NAME_STR, str_val), RECV_ATTR_DATA(ATTR_NAME_DATA, data_val), RECV_ATTR_HASH(table), + RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val2), ATTR_TYPE_END)) > 4) { vstream_printf("%s %d\n", ATTR_NAME_INT, int_val); vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); @@ -467,6 +491,7 @@ int main(int unused_argc, char **used_argv) for (ht = ht_info_list; *ht; ht++) vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value); myfree((void *) ht_info_list); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val2); } else { vstream_printf("return: %d\n", ret); } diff --git a/postfix/src/util/attr_scan0.ref b/postfix/src/util/attr_scan0.ref index a9e211d82..125faeb59 100644 --- a/postfix/src/util/attr_scan0.ref +++ b/postfix/src/util/attr_scan0.ref @@ -4,6 +4,7 @@ ./attr_print0: send attr data = [data 7 bytes] ./attr_print0: send attr name foo-name value foo-value ./attr_print0: send attr name bar-name value bar-value +./attr_print0: send attr long_number = 4321 ./attr_print0: send attr number = 4711 ./attr_print0: send attr long_number = 1234 ./attr_print0: send attr string = whoopee @@ -21,12 +22,19 @@ ./attr_scan0: input attribute name: data ./attr_scan0: input attribute value: d2hvb3BlZQ== ./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan0: input attribute name: { +./attr_scan0: unknown_stream: wanted attribute: (any attribute name or '}') ./attr_scan0: input attribute name: foo-name ./attr_scan0: input attribute value: foo-value -./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan0: unknown_stream: wanted attribute: (any attribute name or '}') ./attr_scan0: input attribute name: bar-name ./attr_scan0: input attribute value: bar-value -./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan0: unknown_stream: wanted attribute: (any attribute name or '}') +./attr_scan0: input attribute name: } +./attr_scan0: unknown_stream: wanted attribute: long_number +./attr_scan0: input attribute name: long_number +./attr_scan0: input attribute value: 4321 +./attr_scan0: unknown_stream: wanted attribute: (list terminator) ./attr_scan0: input attribute name: (end) ./attr_scan0: unknown_stream: wanted attribute: number ./attr_scan0: input attribute name: number @@ -48,6 +56,7 @@ string whoopee data whoopee (hash) foo-name foo-value (hash) bar-name bar-value +long_number 4321 number 4711 long_number 1234 string whoopee diff --git a/postfix/src/util/attr_scan64.c b/postfix/src/util/attr_scan64.c index 488c4c5f5..0880d83ad 100644 --- a/postfix/src/util/attr_scan64.c +++ b/postfix/src/util/attr_scan64.c @@ -29,7 +29,9 @@ /* (item1 | item2) stands for choice: /* /* .in +5 -/* attr-list :== simple-attr* newline +/* attr-list :== (simple-attr | multi-attr)* newline +/* .br +/* multi-attr :== "{" newline simple-attr* "}" newline /* .br /* simple-attr :== attr-name colon attr-value newline /* .br @@ -100,10 +102,7 @@ /* error. /* .IP "RECV_ATTR_HASH(HTABLE *table)" /* .IP "RECV_ATTR_NAMEVAL(NVTABLE *table)" -/* All further input attributes are processed as string attributes. -/* No specific attribute sequence is enforced. -/* All attributes up to the attribute list terminator are read, -/* but only the first instance of each attribute is stored. +/* Receive a sequence of attribute names and string values. /* There can be no more than 1024 attributes in a hash table. /* .sp /* The attribute string values are stored in the hash table under @@ -111,7 +110,15 @@ /* Values from the input stream are added to the hash table. Existing /* hash table entries are not replaced. /* .sp -/* N.B. This construct must be followed by an ATTR_TYPE_END argument. +/* Note: the SEND_ATTR_HASH or SEND_ATTR_NAMEVAL requests +/* format their payload as a multi-attr sequence (see syntax +/* above). When the receiver's input does not start with a +/* multi-attr delimiter (i.e. the sender did not request +/* SEND_ATTR_HASH or SEND_ATTR_NAMEVAL), the receiver will +/* store all attribute names and values up to the attribute +/* list terminator. In terms of code, this means that the +/* RECV_ATTR_HASH or RECV_ATTR_NAMEVAL request must be followed +/* by ATTR_TYPE_END. /* .IP ATTR_TYPE_END /* This argument terminates the requested attribute list. /* .RE @@ -297,7 +304,8 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) * from the input stream instead. This is secure only when the * resulting table is queried with known to be good attribute names. */ - if (wanted_type != ATTR_TYPE_HASH) { + if (wanted_type != ATTR_TYPE_HASH + && wanted_type != ATTR_TYPE_CLOSE) { wanted_type = va_arg(ap, int); if (wanted_type == ATTR_TYPE_END) { if ((flags & ATTR_FLAG_MORE) != 0) @@ -306,9 +314,6 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) } else if (wanted_type == ATTR_TYPE_HASH) { wanted_name = "(any attribute name or list terminator)"; hash_table = va_arg(ap, HTABLE *); - if (va_arg(ap, int) !=ATTR_TYPE_END) - msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END", - myname); } else if (wanted_type != ATTR_TYPE_FUNC) { wanted_name = va_arg(ap, char *); } @@ -343,7 +348,20 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) /* * See if the caller asks for this attribute. */ + if (wanted_type == ATTR_TYPE_HASH + && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { + wanted_type = ATTR_TYPE_CLOSE; + wanted_name = "(any attribute name or '}')"; + /* Advance in the input stream. */ + continue; + } else if (wanted_type == ATTR_TYPE_CLOSE + && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { + /* Advance in the argument list. */ + wanted_type = -1; + break; + } if (wanted_type == ATTR_TYPE_HASH + || wanted_type == ATTR_TYPE_CLOSE || (wanted_type != ATTR_TYPE_END && strcmp(wanted_name, STR(name_buf)) == 0)) break; @@ -440,6 +458,7 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) return (-1); break; case ATTR_TYPE_HASH: + case ATTR_TYPE_CLOSE: if (ch != ':') { msg_warn("missing value for string attribute %s from %s", STR(name_buf), VSTREAM_PATH(fp)); @@ -468,6 +487,9 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) mystrdup(STR(str_buf))); } break; + case -1: + conversions -= 1; + break; default: msg_panic("%s: unknown type code: %d", myname, wanted_type); } @@ -506,6 +528,7 @@ int main(int unused_argc, char **used_argv) HTABLE_INFO **ht; int int_val; long long_val; + long long_val2; int ret; msg_verbose = 1; @@ -517,6 +540,7 @@ int main(int unused_argc, char **used_argv) RECV_ATTR_STR(ATTR_NAME_STR, str_val), RECV_ATTR_DATA(ATTR_NAME_DATA, data_val), RECV_ATTR_HASH(table), + RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val2), ATTR_TYPE_END)) > 4) { vstream_printf("%s %d\n", ATTR_NAME_INT, int_val); vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); @@ -526,6 +550,7 @@ int main(int unused_argc, char **used_argv) for (ht = ht_info_list; *ht; ht++) vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value); myfree((void *) ht_info_list); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val2); } else { vstream_printf("return: %d\n", ret); } diff --git a/postfix/src/util/attr_scan64.ref b/postfix/src/util/attr_scan64.ref index 0f1915bc7..2fe353f80 100644 --- a/postfix/src/util/attr_scan64.ref +++ b/postfix/src/util/attr_scan64.ref @@ -4,6 +4,7 @@ ./attr_print64: send attr data = [data 7 bytes] ./attr_print64: send attr name foo-name value foo-value ./attr_print64: send attr name bar-name value bar-value +./attr_print64: send attr long_number = 4321 ./attr_print64: send attr number = 4711 ./attr_print64: send attr long_number = 1234 ./attr_print64: send attr string = whoopee @@ -21,12 +22,19 @@ ./attr_scan64: input attribute name: data ./attr_scan64: input attribute value: whoopee ./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan64: input attribute name: { +./attr_scan64: unknown_stream: wanted attribute: (any attribute name or '}') ./attr_scan64: input attribute name: foo-name ./attr_scan64: input attribute value: foo-value -./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan64: unknown_stream: wanted attribute: (any attribute name or '}') ./attr_scan64: input attribute name: bar-name ./attr_scan64: input attribute value: bar-value -./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan64: unknown_stream: wanted attribute: (any attribute name or '}') +./attr_scan64: input attribute name: } +./attr_scan64: unknown_stream: wanted attribute: long_number +./attr_scan64: input attribute name: long_number +./attr_scan64: input attribute value: 4321 +./attr_scan64: unknown_stream: wanted attribute: (list terminator) ./attr_scan64: input attribute name: (end) ./attr_scan64: unknown_stream: wanted attribute: number ./attr_scan64: input attribute name: number @@ -48,6 +56,7 @@ string whoopee data whoopee (hash) foo-name foo-value (hash) bar-name bar-value +long_number 4321 number 4711 long_number 1234 string whoopee diff --git a/postfix/src/util/attr_scan_plain.c b/postfix/src/util/attr_scan_plain.c index 4a99f5cdf..fed76cddd 100644 --- a/postfix/src/util/attr_scan_plain.c +++ b/postfix/src/util/attr_scan_plain.c @@ -29,7 +29,9 @@ /* (item1 | item2) stands for choice: /* /* .in +5 -/* attr-list :== simple-attr* newline +/* attr-list :== (simple-attr | multi-attr)* newline +/* .br +/* multi-attr :== "{" newline simple-attr* "}" newline /* .br /* simple-attr :== attr-name "=" attr-value newline /* .br @@ -98,10 +100,7 @@ /* error. /* .IP "RECV_ATTR_HASH(HTABLE *table)" /* .IP "RECV_ATTR_NAMEVAL(NVTABLE *table)" -/* All further input attributes are processed as string attributes. -/* No specific attribute sequence is enforced. -/* All attributes up to the attribute list terminator are read, -/* but only the first instance of each attribute is stored. +/* Receive a sequence of attribute names and string values. /* There can be no more than 1024 attributes in a hash table. /* .sp /* The attribute string values are stored in the hash table under @@ -109,7 +108,15 @@ /* Values from the input stream are added to the hash table. Existing /* hash table entries are not replaced. /* .sp -/* N.B. This construct must be followed by an ATTR_TYPE_END argument. +/* Note: the SEND_ATTR_HASH or SEND_ATTR_NAMEVAL requests +/* format their payload as a multi-attr sequence (see syntax +/* above). When the receiver's input does not start with a +/* multi-attr delimiter (i.e. the sender did not request +/* SEND_ATTR_HASH or SEND_ATTR_NAMEVAL), the receiver will +/* store all attribute names and values up to the attribute +/* list terminator. In terms of code, this means that the +/* RECV_ATTR_HASH or RECV_ATTR_NAMEVAL request must be followed +/* by ATTR_TYPE_END. /* .IP ATTR_TYPE_END /* This argument terminates the requested attribute list. /* .RE @@ -310,7 +317,8 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) * from the input stream instead. This is secure only when the * resulting table is queried with known to be good attribute names. */ - if (wanted_type != ATTR_TYPE_HASH) { + if (wanted_type != ATTR_TYPE_HASH + && wanted_type != ATTR_TYPE_CLOSE) { wanted_type = va_arg(ap, int); if (wanted_type == ATTR_TYPE_END) { if ((flags & ATTR_FLAG_MORE) != 0) @@ -319,9 +327,6 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) } else if (wanted_type == ATTR_TYPE_HASH) { wanted_name = "(any attribute name or list terminator)"; hash_table = va_arg(ap, HTABLE *); - if (va_arg(ap, int) !=ATTR_TYPE_END) - msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END", - myname); } else if (wanted_type != ATTR_TYPE_FUNC) { wanted_name = va_arg(ap, char *); } @@ -356,7 +361,20 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) /* * See if the caller asks for this attribute. */ + if (wanted_type == ATTR_TYPE_HASH + && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) { + wanted_type = ATTR_TYPE_CLOSE; + wanted_name = "(any attribute name or '}')"; + /* Advance in the input stream. */ + continue; + } else if (wanted_type == ATTR_TYPE_CLOSE + && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) { + /* Advance in the argument list. */ + wanted_type = -1; + break; + } if (wanted_type == ATTR_TYPE_HASH + || wanted_type == ATTR_TYPE_CLOSE || (wanted_type != ATTR_TYPE_END && strcmp(wanted_name, STR(name_buf)) == 0)) break; @@ -428,6 +446,7 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) return (-1); break; case ATTR_TYPE_HASH: + case ATTR_TYPE_CLOSE: if (ch != '=') { msg_warn("missing value for string attribute %s from %s", STR(name_buf), VSTREAM_PATH(fp)); @@ -451,6 +470,9 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) mystrdup(STR(str_buf))); } break; + case -1: + conversions -= 1; + break; default: msg_panic("%s: unknown type code: %d", myname, wanted_type); } @@ -489,6 +511,7 @@ int main(int unused_argc, char **used_argv) HTABLE_INFO **ht; int int_val; long long_val; + long long_val2; int ret; msg_verbose = 1; @@ -500,6 +523,7 @@ int main(int unused_argc, char **used_argv) RECV_ATTR_STR(ATTR_NAME_STR, str_val), RECV_ATTR_DATA(ATTR_NAME_DATA, data_val), RECV_ATTR_HASH(table), + RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val2), ATTR_TYPE_END)) > 4) { vstream_printf("%s %d\n", ATTR_NAME_INT, int_val); vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); @@ -509,6 +533,7 @@ int main(int unused_argc, char **used_argv) for (ht = ht_info_list; *ht; ht++) vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value); myfree((void *) ht_info_list); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val2); } else { vstream_printf("return: %d\n", ret); } diff --git a/postfix/src/util/attr_scan_plain.ref b/postfix/src/util/attr_scan_plain.ref index eee95eb54..6e962c81f 100644 --- a/postfix/src/util/attr_scan_plain.ref +++ b/postfix/src/util/attr_scan_plain.ref @@ -4,6 +4,7 @@ ./attr_print_plain: send attr data = [data 7 bytes] ./attr_print_plain: send attr name foo-name value foo-value ./attr_print_plain: send attr name bar-name value bar-value +./attr_print_plain: send attr long_number = 4321 ./attr_print_plain: send attr number = 4711 ./attr_print_plain: send attr long_number = 1234 ./attr_print_plain: send attr string = whoopee @@ -21,12 +22,19 @@ ./attr_scan_plain: input attribute name: data ./attr_scan_plain: input attribute value: d2hvb3BlZQ== ./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan_plain: input attribute name: { +./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or '}') ./attr_scan_plain: input attribute name: foo-name ./attr_scan_plain: input attribute value: foo-value -./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or '}') ./attr_scan_plain: input attribute name: bar-name ./attr_scan_plain: input attribute value: bar-value -./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or '}') +./attr_scan_plain: input attribute name: } +./attr_scan_plain: unknown_stream: wanted attribute: long_number +./attr_scan_plain: input attribute name: long_number +./attr_scan_plain: input attribute value: 4321 +./attr_scan_plain: unknown_stream: wanted attribute: (list terminator) ./attr_scan_plain: input attribute name: (end) ./attr_scan_plain: unknown_stream: wanted attribute: number ./attr_scan_plain: input attribute name: number @@ -48,6 +56,7 @@ string whoopee data whoopee (hash) foo-name foo-value (hash) bar-name bar-value +long_number 4321 number 4711 long_number 1234 string whoopee diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 497c216fd..5b381af40 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -756,7 +756,7 @@ extern int initgroups(const char *, int); /* * LINUX. */ -#if defined(LINUX2) || defined(LINUX3) +#if defined(LINUX2) || defined(LINUX3) || defined(LINUX4) #define SUPPORTED #include #define UINT32_TYPE unsigned int