From: Wietse Venema Date: Sun, 18 Nov 2001 05:00:00 +0000 (-0500) Subject: snapshot-20011118 X-Git-Tag: v1.1.0~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf47bce10b5ab613660087a1bad91314c4c74f59;p=thirdparty%2Fpostfix.git snapshot-20011118 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index f91617d10..cd8ce6ed0 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -5576,6 +5576,43 @@ Apologies for any names omitted. Bugfix: missing terminator in new attribute-based function call caused signal 11. File: src/cleanup/cleanup.c. + Lame workaround for ESTALE errors with mail delivery over + NFS. Additional bandages were added to the local delivery + agent. However, Wietse maintains that Postfix offers no + guarantee for reliable delivery over NFS. + + Feature: put "warn_if_reject" before an smtpd restriction, + and the restriction logs a warning without rejecting mail. + This makes it easier to configurations in the real world + without having to lose mail. File: smtpd/smtpd_check.c. + +20011107 + + Workaround: in order to get mail past PIX firewall bugs, + the Postfix SMTP client now blocks until the socket send + buffer is empty before sending the final ".". Files: + util/sock_empty_wait.c, smtp/smtp_proto.c. + +20011108 + + Feature: added string-null encoding for internal protocols. + Files: util/attr_print0.c, util/attr_scan0c. + + Feature: configurable parent domain matching for domain + and hostname/address match lists: either .domain or the + domain name itself. + + Feature: added pretend-to-be-behind-PIX mode to the smtp-sink + test program, in order to stress test some PIX bug workaround + code. + +20011109 + + Workaround: Linux and Solaris systems have no reasonable + way to block until a socket drains. On these systems Postfix + simply waits for 10 seconds, in order to work around PIX + "." bugs. File: util/sock_empty_wait.c. + 20011114 Bugfix: reset the smtpd command transaction log between @@ -5588,9 +5625,9 @@ Apologies for any names omitted. 20011116 - Bugfix: consolidated all the command transaction log - resets and eliminated one other case (Victor Duchovny, - Morgan Stanley). File: smtpd/smtpd.c. + Bugfix: consolidated all the command transaction log resets + and eliminated one missing reset (Victor Duchovny, Morgan + Stanley). File: smtpd/smtpd.c. Open problems: @@ -5603,6 +5640,8 @@ Open problems: Medium: smtpd access maps don't understand the recipient delimiter setting. + Low: default domain for appending to unqualified recipients. + Low: The $process_id_directory setting is not used anywhere in Postfix. Problem reported by Michael Smith, texas.net. This should be documented, or better, the code should warn diff --git a/postfix/INSTALL b/postfix/INSTALL index 3d6748521..8c5419b22 100644 --- a/postfix/INSTALL +++ b/postfix/INSTALL @@ -213,8 +213,8 @@ In order to install or upgrade Postfix: - Run the INSTALL.sh script as the super-user: - # make install (interactive version, first time install) - # make install /* -/* DOMAIN_LIST *domain_list_init(pattern_list) +/* DOMAIN_LIST *domain_list_init(flags, pattern_list) +/* int flags; /* const char *pattern_list; /* /* int domain_list_match(list, name) @@ -16,6 +17,8 @@ /* void domain_list_free(list) /* DOMAIN_LIST *list; /* DESCRIPTION +/* This is a convenience wrapper around the match_list module. +/* /* This module implements tests for list membership of a host or /* domain name. /* @@ -30,9 +33,20 @@ /* insensitive. In order to reverse the result, precede a non-file /* name pattern with an exclamation point (!). /* -/* domain_list_init() performs initializations. The argument is a -/* list of domain patterns, or the name of a file containing domain -/* patterns. +/* domain_list_init() performs initializations. The first argument +/* is the bit-wise OR of zero or more of the following: +/* .RS +/* .IP MATCH_FLAG_PARENT +/* The hostname pattern foo.com matches itself and any name below +/* the domain foo.com. +/* .IP MATCH_FLAG_DOTPARENT +/* The hostname pattern foo.com matches itself only. +/* The hostname pattern .foo.com matches any name below the domain +/* foo.com. +/* .RE +/* Specify MATCH_FLAG_NONE to request none of the above. +/* The second argument is a list of domain patterns, or the name of +/* a file containing domain patterns. /* /* domain_list_match() matches the specified host or domain name /* against the specified pattern list. @@ -62,33 +76,11 @@ /* Utility library. */ #include -#include /* Global library. */ #include "domain_list.h" -/* domain_list_init - initialize domain list */ - -DOMAIN_LIST *domain_list_init(const char *patterns) -{ - return (match_list_init(patterns, 1, match_hostname)); -} - -/* domain_list_match - match host against domain list */ - -int domain_list_match(DOMAIN_LIST *list, const char *name) -{ - return (match_list_match(list, name)); -} - -/* domain_list_free - release storage */ - -void domain_list_free(DOMAIN_LIST *list) -{ - match_list_free(list); -} - #ifdef TEST #include diff --git a/postfix/src/global/domain_list.h b/postfix/src/global/domain_list.h index b36f516b4..23f60c789 100644 --- a/postfix/src/global/domain_list.h +++ b/postfix/src/global/domain_list.h @@ -11,14 +11,20 @@ /* DESCRIPTION /* .nf + /* + * Utility library. + */ +#include +#include + /* * External interface. */ -typedef struct MATCH_LIST DOMAIN_LIST; +#define DOMAIN_LIST MATCH_LIST -extern DOMAIN_LIST *domain_list_init(const char *); -extern int domain_list_match(DOMAIN_LIST *, const char *); -extern void domain_list_free(DOMAIN_LIST *); +#define domain_list_init(f, p) match_list_init((f), (p), 1, match_hostname) +#define domain_list_match match_list_match +#define domain_list_free match_list_free /* LICENSE /* .ad diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 21b8ef185..89c8c7885 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -1140,6 +1140,8 @@ extern int var_access_map_code; #define CHECK_RECIP_ACL "check_recipient_access" #define CHECK_ETRN_ACL "check_etrn_access" +#define WARN_IF_REJECT "warn_if_reject" + #define REJECT_MAPS_RBL "reject_maps_rbl" #define VAR_MAPS_RBL_CODE "maps_rbl_reject_code" #define DEF_MAPS_RBL_CODE 554 diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index e42dbe517..f9aa03559 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20011116" +#define DEF_MAIL_VERSION "Snapshot-20011118" extern char *var_mail_version; /* LICENSE diff --git a/postfix/src/global/namadr_list.c b/postfix/src/global/namadr_list.c index 0e6b1bba7..b527ba37d 100644 --- a/postfix/src/global/namadr_list.c +++ b/postfix/src/global/namadr_list.c @@ -6,7 +6,8 @@ /* SYNOPSIS /* #include /* -/* NAMADR_LIST *namadr_list_init(pattern_list) +/* NAMADR_LIST *namadr_list_init(flags, pattern_list) +/* int flags; /* const char *pattern_list; /* /* int namadr_list_match(list, name, addr) @@ -17,6 +18,8 @@ /* void namadr_list_free(list) /* NAMADR_LIST *list; /* DESCRIPTION +/* This is a convenience wrapper around the match_list module. +/* /* This module implements tests for list membership of a /* hostname or network address. /* @@ -35,9 +38,21 @@ /* a pattern, or when any of its parent domains matches a /* pattern. The matching process is case insensitive. /* -/* namadr_list_init() performs initializations. The argument -/* is a list of patterns, or the absolute pathname of a file -/* with patterns. +/* namadr_list_init() performs initializations. The first +/* argument is the bit-wise OR of zero or mor of the +/* following: +/* .RS +/* .IP MATCH_FLAG_PARENT +/* The hostname pattern foo.com matches itself and any name below +/* the domain foo.com. +/* .IP MATCH_FLAG_DOTPARENT +/* The hostname pattern foo.com matches itself only. +/* The hostname pattern .foo.com matches any name below the domain +/* foo.com. +/* .RE +/* Specify MATCH_FLAG_NONE to request none of the above. +/* The second argument is a list of patterns, or the absolute +/* pathname of a file with patterns. /* /* namadr_list_match() matches the specified host name and /* address against the specified list of patterns. @@ -67,33 +82,11 @@ /* Utility library. */ #include -#include /* Global library. */ #include "namadr_list.h" -/* namadr_list_init - initialize domain list */ - -NAMADR_LIST *namadr_list_init(const char *patterns) -{ - return (match_list_init(patterns, 2, match_hostaddr, match_hostname)); -} - -/* namadr_list_match - match host against set of namadr_list patterns */ - -int namadr_list_match(NAMADR_LIST *list, const char *name, const char *addr) -{ - return (match_list_match(list, addr, name)); -} - -/* namadr_list_free - release storage */ - -void namadr_list_free(NAMADR_LIST *list) -{ - match_list_free(list); -} - #ifdef TEST #include @@ -116,22 +109,22 @@ main(int argc, char **argv) msg_vstream_init(argv[0], VSTREAM_ERR); while ((ch = GETOPT(argc, argv, "v")) > 0) { - switch (ch) { - case 'v': - msg_verbose++; - break; - default: - usage(argv[0]); - } + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } } if (argc != optind + 3) - usage(argv[0]); + usage(argv[0]); list = namadr_list_init(argv[optind]); host = argv[optind + 1]; addr = argv[optind + 2]; vstream_printf("%s/%s: %s\n", host, addr, - namadr_list_match(list, host, addr) ? - "YES" : "NO"); + namadr_list_match(list, host, addr) ? + "YES" : "NO"); vstream_fflush(VSTREAM_OUT); namadr_list_free(list); } diff --git a/postfix/src/global/namadr_list.h b/postfix/src/global/namadr_list.h index 186cd66c1..9bf90d768 100644 --- a/postfix/src/global/namadr_list.h +++ b/postfix/src/global/namadr_list.h @@ -7,18 +7,25 @@ /* SUMMARY /* name/address membership /* SYNOPSIS -/* #include +/* #include /* DESCRIPTION /* .nf + /* + * Utility library. + */ +#include +#include + /* * External interface. */ -typedef struct MATCH_LIST NAMADR_LIST; +#define NAMADR_LIST MATCH_LIST -extern NAMADR_LIST *namadr_list_init(const char *); -extern int namadr_list_match(NAMADR_LIST *, const char *, const char *); -extern void namadr_list_free(NAMADR_LIST *); +#define namadr_list_init(f, p) \ + match_list_init((f), (p), 2, match_hostname, match_hostaddr) +#define namadr_list_match match_list_match +#define namadr_list_free match_list_free /* LICENSE /* .ad diff --git a/postfix/src/global/resolve_local.c b/postfix/src/global/resolve_local.c index 42fbae7b3..66730c713 100644 --- a/postfix/src/global/resolve_local.c +++ b/postfix/src/global/resolve_local.c @@ -69,7 +69,7 @@ void resolve_local_init(void) { if (resolve_local_list) msg_panic("resolve_local_init: duplicate initialization"); - resolve_local_list = string_list_init(var_mydest); + resolve_local_list = string_list_init(MATCH_FLAG_NONE, var_mydest); } /* resolve_local - match address against list of local destinations */ diff --git a/postfix/src/global/string_list.c b/postfix/src/global/string_list.c index bcb457e77..c2890faa2 100644 --- a/postfix/src/global/string_list.c +++ b/postfix/src/global/string_list.c @@ -6,7 +6,8 @@ /* SYNOPSIS /* #include /* -/* STRING_LIST *string_list_init(pattern_list) +/* STRING_LIST *string_list_init(flags, pattern_list) +/* int flags; /* const char *pattern_list; /* /* int string_list_match(list, name) @@ -16,6 +17,8 @@ /* void string_list_free(list) /* STRING_LIST *list; /* DESCRIPTION +/* This is a convenience wrapper around the match_list module. +/* /* This module implements tests for list membership of a string. /* /* Patterns are separated by whitespace and/or commas. A pattern @@ -28,8 +31,8 @@ /* In order to reverse the result, precede a non-file name pattern /* with an exclamation point (!). /* -/* string_list_init() performs initializations. The argument is a -/* list of string patterns. +/* string_list_init() performs initializations. The flags argument +/* is ignored; pattern_list specifies a list of string patterns. /* /* string_list_match() matches the specified string against the /* compiled pattern list. @@ -58,33 +61,11 @@ /* Utility library. */ #include -#include /* Global library. */ #include "string_list.h" -/* string_list_init - initialize string list */ - -STRING_LIST *string_list_init(const char *patterns) -{ - return (match_list_init(patterns, 1, match_string)); -} - -/* string_list_match - match string against list */ - -int string_list_match(STRING_LIST * list, const char *string) -{ - return (match_list_match(list, string)); -} - -/* string_list_free - release storage */ - -void string_list_free(STRING_LIST * list) -{ - match_list_free(list); -} - #ifdef TEST #include diff --git a/postfix/src/global/string_list.h b/postfix/src/global/string_list.h index 946ecf1d2..daa9541d2 100644 --- a/postfix/src/global/string_list.h +++ b/postfix/src/global/string_list.h @@ -11,14 +11,20 @@ /* DESCRIPTION /* .nf + /* + * Utility library. + */ +#include +#include + /* * External interface. */ -typedef struct MATCH_LIST STRING_LIST; +#define STRING_LIST MATCH_LIST -extern STRING_LIST *string_list_init(const char *); -extern int string_list_match(STRING_LIST *, const char *); -extern void string_list_free(STRING_LIST *); +#define string_list_init(f, p) match_list_init((f), (p), 1, match_string) +#define string_list_match match_list_match +#define string_list_free match_list_free /* LICENSE /* .ad diff --git a/postfix/src/lmtp/Makefile.in b/postfix/src/lmtp/Makefile.in index 29c313c24..cb8f5637f 100644 --- a/postfix/src/lmtp/Makefile.in +++ b/postfix/src/lmtp/Makefile.in @@ -176,6 +176,8 @@ lmtp_sasl_glue.o: ../../include/split_at.h lmtp_sasl_glue.o: ../../include/name_mask.h lmtp_sasl_glue.o: ../../include/mail_params.h lmtp_sasl_glue.o: ../../include/string_list.h +lmtp_sasl_glue.o: ../../include/match_list.h +lmtp_sasl_glue.o: ../../include/match_ops.h lmtp_sasl_glue.o: ../../include/maps.h lmtp_sasl_glue.o: ../../include/dict.h lmtp_sasl_glue.o: ../../include/vstream.h diff --git a/postfix/src/local/file.c b/postfix/src/local/file.c index e3a530ba8..610b1a61d 100644 --- a/postfix/src/local/file.c +++ b/postfix/src/local/file.c @@ -170,7 +170,7 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) * As the mail system, bounce, defer delivery, or report success. */ if (status != 0) { - status = (errno == EAGAIN || errno == ENOSPC ? + status = (errno == EAGAIN || errno == ENOSPC || errno == ESTALE ? defer_append : bounce_append) (BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), "cannot append message to destination file %s: %s", diff --git a/postfix/src/local/mailbox.c b/postfix/src/local/mailbox.c index 667a65e19..a07d11389 100644 --- a/postfix/src/local/mailbox.c +++ b/postfix/src/local/mailbox.c @@ -203,7 +203,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) * As the mail system, bounce, defer delivery, or report success. */ if (status != 0) { - status = (errno == EAGAIN || errno == ENOSPC ? + status = (errno == EAGAIN || errno == ENOSPC || errno == ESTALE ? defer_append : bounce_append) (BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), "cannot access mailbox %s for user %s. %s", @@ -283,18 +283,14 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) */ #define LAST_CHAR(s) (s[strlen(s) - 1]) - if (*var_mailbox_cmd_maps) { - if (cmd_maps == 0) - cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps, - DICT_FLAG_LOCK); - if ((map_command = maps_find(cmd_maps, state.msg_attr.user, - DICT_FLAG_FIXED)) != 0) { - status = deliver_command(state, usr_attr, map_command); - } else { - msg_warn("user %s not found in %s", - state.msg_attr.user, var_mailbox_cmd_maps); - return (NO); - } + if (*var_mailbox_cmd_maps && cmd_maps == 0) + cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps, + DICT_FLAG_LOCK); + + if (*var_mailbox_cmd_maps + && (map_command = maps_find(cmd_maps, state.msg_attr.user, + DICT_FLAG_FIXED)) != 0) { + status = deliver_command(state, usr_attr, map_command); } else if (*var_mailbox_command) { status = deliver_command(state, usr_attr, var_mailbox_command); } else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') { diff --git a/postfix/src/local/maildir.c b/postfix/src/local/maildir.c index e9bd0de85..195e0bfdb 100644 --- a/postfix/src/local/maildir.c +++ b/postfix/src/local/maildir.c @@ -153,7 +153,8 @@ int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) set_eugid(var_owner_uid, var_owner_gid); if (status) - status = (errno == ENOSPC ? defer_append : bounce_append) + status = (errno == ENOSPC || errno == ESTALE ? + defer_append : bounce_append) (BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), "maildir delivery failed: %s", vstring_str(why)); else diff --git a/postfix/src/postconf/Makefile.in b/postfix/src/postconf/Makefile.in index d9559dd9a..b5cfc89ac 100644 --- a/postfix/src/postconf/Makefile.in +++ b/postfix/src/postconf/Makefile.in @@ -23,7 +23,10 @@ $(PROG): $(OBJS) $(LIBS) ../../conf/main.cf.default: $(PROG) Makefile rm -f $@ - ./$(PROG) -d |egrep -v '^(myhostname|mydomain|mynetworks) ' >$@ + (echo "# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE"; \ + echo "# TEXT HERE JUST SHOWS DEFAULT SETTINGS BUILT INTO POSTFIX."; \ + echo "#"; \ + ./$(PROG) -d) |egrep -v '^(myhostname|mydomain|mynetworks) ' >$@ Makefile: Makefile.in (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@ diff --git a/postfix/src/qmqpd/Makefile.in b/postfix/src/qmqpd/Makefile.in index b5fa478cc..e03398ee1 100644 --- a/postfix/src/qmqpd/Makefile.in +++ b/postfix/src/qmqpd/Makefile.in @@ -89,6 +89,8 @@ qmqpd.o: ../../include/mail_conf.h qmqpd.o: ../../include/debug_peer.h qmqpd.o: ../../include/mail_stream.h qmqpd.o: ../../include/namadr_list.h +qmqpd.o: ../../include/match_list.h +qmqpd.o: ../../include/match_ops.h qmqpd.o: ../../include/quote_822_local.h qmqpd.o: ../../include/mail_server.h qmqpd.o: qmqpd.h diff --git a/postfix/src/qmqpd/qmqpd.c b/postfix/src/qmqpd/qmqpd.c index 5b1d1d1b0..e17eed0f1 100644 --- a/postfix/src/qmqpd/qmqpd.c +++ b/postfix/src/qmqpd/qmqpd.c @@ -643,7 +643,7 @@ static void pre_accept(char *unused_name, char **unused_argv) static void pre_jail_init(char *unused_name, char **unused_argv) { debug_peer_init(); - qmqpd_clients = namadr_list_init(var_qmqpd_clients); + qmqpd_clients = namadr_list_init(MATCH_FLAG_PARENT, var_qmqpd_clients); } /* main - the main program */ diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index 87ba5ac14..035cd5aca 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -151,6 +151,7 @@ smtp_proto.o: ../../include/vstream.h smtp_proto.o: ../../include/vstring_vstream.h smtp_proto.o: ../../include/stringops.h smtp_proto.o: ../../include/mymalloc.h +smtp_proto.o: ../../include/iostuff.h smtp_proto.o: ../../include/mail_params.h smtp_proto.o: ../../include/smtp_stream.h smtp_proto.o: ../../include/mail_queue.h @@ -179,6 +180,8 @@ smtp_sasl_glue.o: ../../include/split_at.h smtp_sasl_glue.o: ../../include/name_mask.h smtp_sasl_glue.o: ../../include/mail_params.h smtp_sasl_glue.o: ../../include/string_list.h +smtp_sasl_glue.o: ../../include/match_list.h +smtp_sasl_glue.o: ../../include/match_ops.h smtp_sasl_glue.o: ../../include/maps.h smtp_sasl_glue.o: ../../include/dict.h smtp_sasl_glue.o: ../../include/vstream.h diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index b26095fd8..3267d5eae 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -82,6 +82,7 @@ #include #include #include +#include /* Global library. */ @@ -178,8 +179,11 @@ int smtp_helo(SMTP_STATE *state) * does not span a packet boundary. This hurts performance so it is not * on by default. */ - if (resp->str[strspn(resp->str, "20 *\t\n")] == 0) + if (resp->str[strspn(resp->str, "20 *\t\n")] == 0) { + msg_info("enabling PIX . workaround for %s", + session->namaddr); state->features |= SMTP_FEATURE_MAYBEPIX; + } /* * See if we are talking to ourself. This should not be possible with the @@ -196,9 +200,9 @@ int smtp_helo(SMTP_STATE *state) } else if (strcasecmp(word, "ESMTP") == 0) state->features |= SMTP_FEATURE_ESMTP; } - if (var_smtp_always_ehlo) + if (var_smtp_always_ehlo && (state->features & SMTP_FEATURE_MAYBEPIX) == 0) state->features |= SMTP_FEATURE_ESMTP; - if (var_smtp_never_ehlo) + if (var_smtp_never_ehlo || (state->features & SMTP_FEATURE_MAYBEPIX) != 0) state->features &= ~SMTP_FEATURE_ESMTP; /* @@ -657,9 +661,11 @@ int smtp_xfer(SMTP_STATE *state) if (prev_type == REC_TYPE_CONT) /* missing newline at end */ smtp_fputs("", 0, session->stream); - if ((state->features & SMTP_FEATURE_ESMTP) == 0 - && (state->features & SMTP_FEATURE_MAYBEPIX) != 0) + if ((state->features & SMTP_FEATURE_MAYBEPIX) != 0) { vstream_fflush(session->stream);/* hurts performance */ + sock_empty_wait(vstream_fileno(session->stream), + session->stream->timeout / 2); + } if (vstream_ferror(state->src)) msg_fatal("queue file read error"); if (rec_type != REC_TYPE_XTRA) diff --git a/postfix/src/smtpd/Makefile.in b/postfix/src/smtpd/Makefile.in index e8181359b..57016b95c 100644 --- a/postfix/src/smtpd/Makefile.in +++ b/postfix/src/smtpd/Makefile.in @@ -176,6 +176,8 @@ smtpd_check.o: ../../include/htable.h smtpd_check.o: ../../include/ctable.h smtpd_check.o: ../../include/dns.h smtpd_check.o: ../../include/namadr_list.h +smtpd_check.o: ../../include/match_list.h +smtpd_check.o: ../../include/match_ops.h smtpd_check.o: ../../include/domain_list.h smtpd_check.o: ../../include/mail_params.h smtpd_check.o: ../../include/canon_addr.h @@ -208,6 +210,8 @@ smtpd_sasl_glue.o: ../../include/sys_defs.h smtpd_sasl_glue.o: ../../include/msg.h smtpd_sasl_glue.o: ../../include/mymalloc.h smtpd_sasl_glue.o: ../../include/namadr_list.h +smtpd_sasl_glue.o: ../../include/match_list.h +smtpd_sasl_glue.o: ../../include/match_ops.h smtpd_sasl_glue.o: ../../include/name_mask.h smtpd_sasl_glue.o: ../../include/mail_params.h smtpd_sasl_glue.o: ../../include/smtp_stream.h diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index e2003ffd2..db4a0b25b 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -1281,7 +1281,7 @@ static void chat_reset(SMTPD_STATE *state) * report problems when running in stand-alone mode: postmaster notices * require availability of the cleanup service. */ - if (state->history != 0 && state->client != VSTREAM_IN + if (state->history != 0 && SMTPD_STAND_ALONE(state) == 0 && (state->error_mask & state->notify_mask)) smtpd_chat_notify(state); smtpd_chat_reset(state); diff --git a/postfix/src/smtpd/smtpd.h b/postfix/src/smtpd/smtpd.h index 29fb3d2d7..20386ad73 100644 --- a/postfix/src/smtpd/smtpd.h +++ b/postfix/src/smtpd/smtpd.h @@ -76,6 +76,7 @@ typedef struct SMTPD_STATE { VSTRING *sasl_encoded; VSTRING *sasl_decoded; #endif + int warn_if_reject; } SMTPD_STATE; extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *); diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index d558c37c3..83b213dd2 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -452,9 +452,14 @@ static int has_required(ARGV *restrictions, char **required) * Recursively check list membership. */ for (rest = restrictions->argv; *rest; rest++) { - for (reqd = required; *reqd; reqd++) + if (strcmp(*rest, WARN_IF_REJECT) == 0 && rest[1] != 0) { + rest += 1; + continue; + } + for (reqd = required; *reqd; reqd++) { if (strcmp(*rest, *reqd) == 0) return (1); + } if ((expansion = (ARGV *) htable_find(smtpd_rest_classes, *rest)) != 0) if (has_required(expansion, required)) return (1); @@ -481,8 +486,9 @@ static void fail_required(char *name, char **required) */ example = vstring_alloc(10); for (reqd = required; *reqd; reqd++) - vstring_sprintf_append(example, "%s ", *reqd); - msg_fatal("parameter \"%s\": specify at least one explicit instance of: %s", + vstring_sprintf_append(example, "%s%s", *reqd, + reqd[1] == 0 ? "" : reqd[2] == 0 ? " or " : ", "); + msg_fatal("parameter \"%s\": specify at least one working instance of: %s", name, STR(example)); } @@ -504,9 +510,9 @@ void smtpd_check_init(void) /* * Pre-open access control lists before going to jail. */ - mynetworks = namadr_list_init(var_mynetworks); - relay_domains = domain_list_init(var_relay_domains); - perm_mx_networks = namadr_list_init(var_perm_mx_networks); + mynetworks = namadr_list_init(MATCH_FLAG_PARENT, var_mynetworks); + relay_domains = domain_list_init(MATCH_FLAG_PARENT, var_relay_domains); + perm_mx_networks = namadr_list_init(MATCH_FLAG_PARENT, var_perm_mx_networks); /* * Pre-parse and pre-open the recipient maps. @@ -585,6 +591,20 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, char *format,...) { va_list ap; + int warn_if_reject; + const char *whatsup; + + /* + * Do not reject mail if we were asked to warn only. However, + * configuration errors cannot be converted into warnings. + */ + if (state->warn_if_reject && error_class != MAIL_ERROR_SOFTWARE) { + warn_if_reject = 1; + whatsup = "reject_warning"; + } else { + warn_if_reject = 0; + whatsup = "reject"; + } /* * Update the error class mask, and format the response. XXX What about @@ -634,22 +654,22 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, * rejected. Print the request, client name/address, and response. */ if (state->recipient && state->sender) { - msg_info("reject: %s from %s: %s; from=<%s> to=<%s>", - state->where, state->namaddr, STR(error_text), + msg_info("%s: %s from %s: %s; from=<%s> to=<%s>", + whatsup, state->where, state->namaddr, STR(error_text), state->sender, state->recipient); } else if (state->recipient) { - msg_info("reject: %s from %s: %s; to=<%s>", - state->where, state->namaddr, STR(error_text), + msg_info("%s: %s from %s: %s; to=<%s>", + whatsup, state->where, state->namaddr, STR(error_text), state->recipient); } else if (state->sender) { - msg_info("reject: %s from %s: %s; from=<%s>", - state->where, state->namaddr, STR(error_text), + msg_info("%s: %s from %s: %s; from=<%s>", + whatsup, state->where, state->namaddr, STR(error_text), state->sender); } else { - msg_info("reject: %s from %s: %s", - state->where, state->namaddr, STR(error_text)); + msg_info("%s: %s from %s: %s", + whatsup, state->where, state->namaddr, STR(error_text)); } - return (SMTPD_CHECK_REJECT); + return (warn_if_reject ? 0 : SMTPD_CHECK_REJECT); } /* reject_dict_retry - reject with temporary failure if dict lookup fails */ @@ -1824,6 +1844,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, int status = 0; ARGV *list; int found; + int saved_recursion = state->recursion; if (msg_verbose) msg_info("%s: START", myname); @@ -1833,6 +1854,15 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, if (msg_verbose) msg_info("%s: name=%s", myname, name); + /* + * Pseudo restrictions. + */ + if (strcasecmp(name, WARN_IF_REJECT) == 0) { + if (state->warn_if_reject == 0) + state->warn_if_reject = state->recursion; + continue; + } + /* * Spoof the is_map_command() routine, so that we do not have to make * special cases for the implicit short-hand access map notation. @@ -1847,14 +1877,14 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, */ if (strcasecmp(name, PERMIT_ALL) == 0) { status = SMTPD_CHECK_OK; - if (cpp[1] != 0) + if (cpp[1] != 0 && state->warn_if_reject == 0) msg_warn("restriction `%s' after `%s' is ignored", cpp[1], PERMIT_ALL); } else if (strcasecmp(name, REJECT_ALL) == 0) { status = smtpd_check_reject(state, MAIL_ERROR_POLICY, "%d <%s>: %s rejected: Access denied", var_reject_code, reply_name, reply_class); - if (cpp[1] != 0) + if (cpp[1] != 0 && state->warn_if_reject == 0) msg_warn("restriction `%s' after `%s' is ignored", cpp[1], REJECT_ALL); } else if (strcasecmp(name, REJECT_UNAUTH_PIPE) == 0) { @@ -1963,7 +1993,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, if (state->recipient) status = check_relay_domains(state, state->recipient, state->recipient, SMTPD_NAME_RECIPIENT); - if (cpp[1] != 0) + if (cpp[1] != 0 && state->warn_if_reject == 0) msg_warn("restriction `%s' after `%s' is ignored", cpp[1], CHECK_RELAY_DOMAINS); #ifdef USE_SASL_AUTH @@ -2011,12 +2041,17 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions, if (msg_verbose) msg_info("%s: name=%s status=%d", myname, name, status); + if (state->warn_if_reject >= state->recursion) + state->warn_if_reject = 0; + if (status != 0) break; } if (msg_verbose && name == 0) msg_info("%s: END", myname); + state->recursion = saved_recursion; + return (status); } @@ -2035,7 +2070,7 @@ char *smtpd_check_client(SMTPD_STATE *state) /* * Apply restrictions in the order as specified. */ - state->recursion = 0; + state->recursion = 1; status = setjmp(smtpd_check_buf); if (status == 0 && client_restrctions->argc) status = generic_checks(state, client_restrctions, state->namaddr, @@ -2081,7 +2116,7 @@ char *smtpd_check_helo(SMTPD_STATE *state, char *helohost) /* * Apply restrictions in the order as specified. */ - state->recursion = 0; + state->recursion = 1; status = setjmp(smtpd_check_buf); if (status == 0 && helo_restrctions->argc) status = generic_checks(state, helo_restrctions, state->helo_name, @@ -2117,7 +2152,7 @@ char *smtpd_check_mail(SMTPD_STATE *state, char *sender) /* * Apply restrictions in the order as specified. */ - state->recursion = 0; + state->recursion = 1; status = setjmp(smtpd_check_buf); if (status == 0 && mail_restrctions->argc) status = generic_checks(state, mail_restrctions, sender, @@ -2171,7 +2206,7 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) /* * Apply restrictions in the order as specified. */ - state->recursion = 0; + state->recursion = 1; status = setjmp(smtpd_check_buf); if (status == 0 && rcpt_restrctions->argc) status = generic_checks(state, rcpt_restrctions, @@ -2216,7 +2251,7 @@ char *smtpd_check_etrn(SMTPD_STATE *state, char *domain) /* * Apply restrictions in the order as specified. */ - state->recursion = 0; + state->recursion = 1; status = setjmp(smtpd_check_buf); if (status == 0 && etrn_restrctions->argc) status = generic_checks(state, etrn_restrctions, domain, @@ -2780,13 +2815,13 @@ int main(int argc, char **argv) } if (strcasecmp(args->argv[0], "mynetworks") == 0) { namadr_list_free(mynetworks); - mynetworks = namadr_list_init(args->argv[1]); + mynetworks = namadr_list_init(MATCH_FLAG_PARENT, args->argv[1]); resp = 0; break; } if (strcasecmp(args->argv[0], "relay_domains") == 0) { domain_list_free(relay_domains); - relay_domains = domain_list_init(args->argv[1]); + relay_domains = domain_list_init(MATCH_FLAG_PARENT, args->argv[1]); resp = 0; break; } diff --git a/postfix/src/smtpd/smtpd_check.in b/postfix/src/smtpd/smtpd_check.in index f7c3cdd70..44f969ce9 100644 --- a/postfix/src/smtpd/smtpd_check.in +++ b/postfix/src/smtpd/smtpd_check.in @@ -6,6 +6,7 @@ smtpd_delay_reject 0 mynetworks 127.0.0.0/8,168.100.189.0/28 relay_domains porcupine.org +maps_rbl_domains blackholes.mail-abuse.org # # Test the client restrictions. # diff --git a/postfix/src/smtpd/smtpd_check.in2 b/postfix/src/smtpd/smtpd_check.in2 index 0eaf8c02b..2b9000267 100644 --- a/postfix/src/smtpd/smtpd_check.in2 +++ b/postfix/src/smtpd/smtpd_check.in2 @@ -6,6 +6,7 @@ smtpd_delay_reject 0 mynetworks 127.0.0.0/8,168.100.189.0/28 relay_domains porcupine.org +maps_rbl_domains blackholes.mail-abuse.org # # Test the client restrictions. # diff --git a/postfix/src/smtpd/smtpd_check.ref b/postfix/src/smtpd/smtpd_check.ref index b63c3f705..121c9e32a 100644 --- a/postfix/src/smtpd/smtpd_check.ref +++ b/postfix/src/smtpd/smtpd_check.ref @@ -9,6 +9,8 @@ OK OK >>> relay_domains porcupine.org OK +>>> maps_rbl_domains blackholes.mail-abuse.org +OK >>> # >>> # Test the client restrictions. >>> # diff --git a/postfix/src/smtpd/smtpd_check.ref2 b/postfix/src/smtpd/smtpd_check.ref2 index c8d014f60..2bd382663 100644 --- a/postfix/src/smtpd/smtpd_check.ref2 +++ b/postfix/src/smtpd/smtpd_check.ref2 @@ -9,6 +9,8 @@ OK OK >>> relay_domains porcupine.org OK +>>> maps_rbl_domains blackholes.mail-abuse.org +OK >>> # >>> # Test the client restrictions. >>> # diff --git a/postfix/src/smtpd/smtpd_state.c b/postfix/src/smtpd/smtpd_state.c index ca4929634..6e7236543 100644 --- a/postfix/src/smtpd/smtpd_state.c +++ b/postfix/src/smtpd/smtpd_state.c @@ -91,6 +91,7 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream) state->recursion = 0; state->msg_size = 0; state->junk_cmds = 0; + state->warn_if_reject = 0; #ifdef USE_SASL_AUTH if (SMTPD_STAND_ALONE(state)) diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c index 7eb997446..24a5e2784 100644 --- a/postfix/src/smtpstone/smtp-sink.c +++ b/postfix/src/smtpstone/smtp-sink.c @@ -28,6 +28,9 @@ /* Terminate after \fIcount\fR sessions. This is for testing purposes. /* .IP \fB-p\fR /* Disable ESMTP command pipelining. +/* .IP \fB-P\fR +/* Change the server greeting so that it appears to come through +/* a CISCO PIX system. /* .IP \fB-v\fR /* Show the SMTP conversations. /* .IP "\fB-w \fIdelay\fR" @@ -114,6 +117,7 @@ static int max_count; static int disable_pipelining; static int fixed_delay; static int enable_lmtp; +static int pretend_pix; /* ehlo_response - respond to EHLO command */ @@ -443,6 +447,9 @@ static void connect_event(int unused_event, char *context) state->read = command_read; state->data_state = ST_ANY; smtp_timeout_setup(state->stream, var_tmout); +if (pretend_pix) + smtp_printf(state->stream, "220 ********"); +else smtp_printf(state->stream, "220 %s ESMTP", var_myhostname); event_enable_read(fd, read_event, (char *) state); } @@ -452,7 +459,7 @@ static void connect_event(int unused_event, char *context) static void usage(char *myname) { - msg_fatal("usage: %s [-cLpv] [-n count] [-w delay] [host]:port backlog", myname); + msg_fatal("usage: %s [-cLpPv] [-n count] [-w delay] [host]:port backlog", myname); } int main(int argc, char **argv) @@ -469,7 +476,7 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "cLn:pvw:")) > 0) { + while ((ch = GETOPT(argc, argv, "cLn:pPvw:")) > 0) { switch (ch) { case 'c': count++; @@ -483,6 +490,9 @@ int main(int argc, char **argv) case 'p': disable_pipelining = 1; break; + case 'P': + pretend_pix=1; + break; case 'v': msg_verbose++; break; diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index b31dc93aa..eb0523dcd 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -23,8 +23,8 @@ SRCS = argv.c argv_split.c basename.c binhash.c chroot_uid.c \ clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \ sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \ hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \ - sane_socketpair.c myrand.c netstring.c ctable.c attr_print.c intv.c \ - attr_scan.c base64_code.c + sane_socketpair.c myrand.c netstring.c ctable.c attr_print64.c intv.c \ + attr_scan64.c base64_code.c sock_empty_wait.c attr_print0.c attr_scan0.c OBJS = argv.o argv_split.o basename.o binhash.o chroot_uid.o \ close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \ dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ @@ -49,8 +49,8 @@ OBJS = argv.o argv_split.o basename.o binhash.o chroot_uid.o \ clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \ sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \ hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \ - sane_socketpair.o myrand.o netstring.o ctable.o attr_print.o intv.o \ - attr_scan.o base64_code.o + sane_socketpair.o myrand.o netstring.o ctable.o attr_print64.o intv.o \ + attr_scan64.o base64_code.o sock_empty_wait.o attr_print0.o attr_scan0.o HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \ dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \ @@ -84,7 +84,8 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ mystrtok sigdelay translit valid_hostname vstream_popen \ vstring vstring_vstream doze select_bug stream_test mac_expand \ watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \ - inet_addr_list attr_print attr_scan base64_code + inet_addr_list attr_print64 attr_scan64 base64_code attr_print0 \ + attr_scan0 LIB_DIR = ../../lib INC_DIR = ../../include @@ -288,12 +289,12 @@ inet_addr_list: $(LIB) $@.o $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o -attr_print: $(LIB) $@.o +attr_print64: $(LIB) $@.o mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o -attr_scan: $(LIB) $@.o +attr_scan64: $(LIB) $@.o mv $@.o junk $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o @@ -303,6 +304,16 @@ base64_code: $(LIB) $@.o $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o +attr_print0: $(LIB) $@.o + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + +attr_scan0: $(LIB) $@.o + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + depend: $(MAKES) (sed '1,/^# do not edit/!d' Makefile.in; \ set -e; for i in [a-z][a-z0-9]*.c; do \ @@ -316,7 +327,7 @@ stream_test: stream_test.c $(LIB) tests: valid_hostname_test mac_expand_test dict_test unescape_test \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \ - attr_scan_test + attr_scan64_test attr_scan0_test valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref ./valid_hostname valid_hostname.tmp @@ -352,10 +363,15 @@ inet_addr_list_test: inet_addr_list base64_code_test: base64_code ./base64_code -attr_scan_test: attr_print attr_scan attr_scan.ref - (./attr_print 2>&3 | (sleep 1; ./attr_scan)) >attr_scan.tmp 2>&1 3>&1 - diff attr_scan.ref attr_scan.tmp - rm -f attr_scan.tmp +attr_scan64_test: attr_print64 attr_scan64 attr_scan64.ref + (./attr_print64 2>&3 | (sleep 1; ./attr_scan64)) >attr_scan64.tmp 2>&1 3>&1 + diff attr_scan64.ref attr_scan64.tmp + rm -f attr_scan64.tmp + +attr_scan0_test: attr_print0 attr_scan0 attr_scan0.ref + (./attr_print0 2>&3 | (sleep 1; ./attr_scan0)) >attr_scan0.tmp 2>&1 3>&1 + diff attr_scan0.ref attr_scan0.tmp + rm -f attr_scan0.tmp DB_TYPE = `../postconf/postconf -h default_database_type` @@ -391,6 +407,24 @@ attr_print.o: htable.h attr_print.o: base64_code.h attr_print.o: vstring.h attr_print.o: attr.h +attr_print0.o: attr_print0.c +attr_print0.o: sys_defs.h +attr_print0.o: msg.h +attr_print0.o: mymalloc.h +attr_print0.o: vstream.h +attr_print0.o: vbuf.h +attr_print0.o: htable.h +attr_print0.o: attr.h +attr_print64.o: attr_print64.c +attr_print64.o: sys_defs.h +attr_print64.o: msg.h +attr_print64.o: mymalloc.h +attr_print64.o: vstream.h +attr_print64.o: vbuf.h +attr_print64.o: htable.h +attr_print64.o: base64_code.h +attr_print64.o: vstring.h +attr_print64.o: attr.h attr_scan.o: attr_scan.c attr_scan.o: sys_defs.h attr_scan.o: msg.h @@ -401,6 +435,26 @@ attr_scan.o: vstring.h attr_scan.o: htable.h attr_scan.o: base64_code.h attr_scan.o: attr.h +attr_scan0.o: attr_scan0.c +attr_scan0.o: sys_defs.h +attr_scan0.o: msg.h +attr_scan0.o: mymalloc.h +attr_scan0.o: vstream.h +attr_scan0.o: vbuf.h +attr_scan0.o: vstring.h +attr_scan0.o: vstring_vstream.h +attr_scan0.o: htable.h +attr_scan0.o: attr.h +attr_scan64.o: attr_scan64.c +attr_scan64.o: sys_defs.h +attr_scan64.o: msg.h +attr_scan64.o: mymalloc.h +attr_scan64.o: vstream.h +attr_scan64.o: vbuf.h +attr_scan64.o: vstring.h +attr_scan64.o: htable.h +attr_scan64.o: base64_code.h +attr_scan64.o: attr.h base64_code.o: base64_code.c base64_code.o: sys_defs.h base64_code.o: msg.h @@ -782,6 +836,7 @@ match_list.o: vstring_vstream.h match_list.o: stringops.h match_list.o: argv.h match_list.o: dict.h +match_list.o: match_ops.h match_list.o: match_list.h match_ops.o: match_ops.c match_ops.o: sys_defs.h @@ -979,6 +1034,10 @@ skipblanks.o: sys_defs.h skipblanks.o: stringops.h skipblanks.o: vstring.h skipblanks.o: vbuf.h +sock_empty_wait.o: sock_empty_wait.c +sock_empty_wait.o: sys_defs.h +sock_empty_wait.o: msg.h +sock_empty_wait.o: iostuff.h spawn_command.o: spawn_command.c spawn_command.o: sys_defs.h spawn_command.o: msg.h diff --git a/postfix/src/util/attr.h b/postfix/src/util/attr.h index 1033b53b3..8c440e147 100644 --- a/postfix/src/util/attr.h +++ b/postfix/src/util/attr.h @@ -40,17 +40,34 @@ #define ATTR_FLAG_STRICT (ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA) #define ATTR_FLAG_ALL (07) +#define attr_print attr_print0 +#define attr_vprint attr_vprint0 +#define attr_scan attr_scan0 +#define attr_vscan attr_vscan0 + + /* + * attr_print64.c. + */ +extern int attr_print64(VSTREAM *, int,...); +extern int attr_vprint64(VSTREAM *, int, va_list); + + /* + * attr_scan64.c. + */ +extern int attr_scan64(VSTREAM *, int,...); +extern int attr_vscan64(VSTREAM *, int, va_list); + /* - * attr_print.c. + * attr_print0.c. */ -extern int attr_print(VSTREAM *, int,...); -extern int attr_vprint(VSTREAM *, int, va_list); +extern int attr_print0(VSTREAM *, int,...); +extern int attr_vprint0(VSTREAM *, int, va_list); /* - * attr_scan.c. + * attr_scan0.c. */ -extern int attr_scan(VSTREAM *, int,...); -extern int attr_vscan(VSTREAM *, int, va_list); +extern int attr_scan0(VSTREAM *, int,...); +extern int attr_vscan0(VSTREAM *, int, va_list); /* * Attribute names for testing the compatibility of the read and write diff --git a/postfix/src/util/attr_print0.c b/postfix/src/util/attr_print0.c new file mode 100644 index 000000000..287340157 --- /dev/null +++ b/postfix/src/util/attr_print0.c @@ -0,0 +1,194 @@ +/*++ +/* NAME +/* attr_print0 3 +/* SUMMARY +/* send attributes over byte stream +/* SYNOPSIS +/* #include +/* +/* int attr_print0(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vprint0(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_print0() takes zero or more (name, value) simple attributes +/* or (name, count, value) list attributes, and converts its input +/* to a byte stream that can be recovered with attr_scan0(). The stream +/* is not flushed. +/* +/* attr_vprint0() provides an alternate interface that is convenient +/* for calling from within variadoc functions. +/* +/* Attributes are sent in the requested order as specified with the +/* attr_print0() argument list. This routine satisfies the formatting +/* rules as outlined in attr_scan0(3). +/* +/* Arguments: +/* .IP fp +/* Stream to write the result to. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MORE +/* After sending the requested attributes, leave the output stream in +/* a state that is usable for more attribute sending operations on +/* the same output attribute list. +/* By default, attr_print0() automatically appends an attribute list +/* terminator when it has sent the last requested attribute. +/* .RE +/* .IP type +/* The type determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int)" +/* This argument is followed by an attribute name and an integer. +/* .IP "ATTR_TYPE_STR (char *, char *)" +/* This argument is followed by an attribute name and a null-terminated +/* string. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* The content of the hash table is sent as a sequence of string-valued +/* attributes with names equal to the hash table lookup key. +/* .IP ATTR_TYPE_END +/* This terminates the attribute list. +/* .RE +/* DIAGNOSTICS +/* The result value is 0 in case of success, VSTREAM_EOF in case +/* of trouble. +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_scan0(3) recover attributes from byte stream +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* attr_vprint0 - send attribute list to stream */ + +int attr_vprint0(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_print0"; + int attr_type; + char *attr_name; + unsigned int_val; + char *str_val; + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Iterate over all (type, name, value) triples, and produce output on + * the fly. + */ + while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) { + switch (attr_type) { + case ATTR_TYPE_NUM: + attr_name = va_arg(ap, char *); + vstream_fwrite(fp, attr_name, strlen(attr_name) + 1); + int_val = va_arg(ap, int); + vstream_fprintf(fp, "%u", (unsigned) int_val); + VSTREAM_PUTC('\0', fp); + if (msg_verbose) + msg_info("send attr %s = %u", attr_name, int_val); + break; + case ATTR_TYPE_STR: + attr_name = va_arg(ap, char *); + vstream_fwrite(fp, attr_name, strlen(attr_name) + 1); + str_val = va_arg(ap, char *); + vstream_fwrite(fp, str_val, strlen(str_val) + 1); + if (msg_verbose) + msg_info("send attr %s = %s", attr_name, str_val); + break; + case ATTR_TYPE_HASH: + 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); + vstream_fwrite(fp, ht[0]->value, strlen(ht[0]->value) + 1); + if (msg_verbose) + msg_info("send attr name %s value %s", + ht[0]->key, ht[0]->value); + } + myfree((char *) ht_info_list); + break; + default: + msg_panic("%s: unknown type code: %d", myname, attr_type); + } + } + if ((flags & ATTR_FLAG_MORE) == 0) + VSTREAM_PUTC('\0', fp); + return (vstream_ferror(fp)); +} + +int attr_print0(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vprint0(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan0 test + * program. + */ +#include + +int main(int unused_argc, char **argv) +{ + HTABLE *table = htable_create(1); + + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_verbose = 1; + htable_enter(table, "foo-name", mystrdup("foo-value")); + htable_enter(table, "bar-name", mystrdup("bar-value")); + attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_HASH, table, + ATTR_TYPE_END); + attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_END); + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + htable_free(table, myfree); + return (0); +} + +#endif diff --git a/postfix/src/util/attr_print64.c b/postfix/src/util/attr_print64.c new file mode 100644 index 000000000..011d542a0 --- /dev/null +++ b/postfix/src/util/attr_print64.c @@ -0,0 +1,227 @@ +/*++ +/* NAME +/* attr_print64 3 +/* SUMMARY +/* send attributes over byte stream +/* SYNOPSIS +/* #include +/* +/* int attr_print64(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vprint64(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_print64() takes zero or more (name, value) simple attributes +/* or (name, count, value) list attributes, and converts its input +/* to a byte stream that can be recovered with attr_scan64(). The stream +/* is not flushed. +/* +/* attr_vprint64() provides an alternate interface that is convenient +/* for calling from within variadoc functions. +/* +/* Attributes are sent in the requested order as specified with the +/* attr_print64() argument list. This routine satisfies the formatting +/* rules as outlined in attr_scan64(3). +/* +/* Arguments: +/* .IP fp +/* Stream to write the result to. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MORE +/* After sending the requested attributes, leave the output stream in +/* a state that is usable for more attribute sending operations on +/* the same output attribute list. +/* By default, attr_print64() automatically appends an attribute list +/* terminator when it has sent the last requested attribute. +/* .RE +/* .IP type +/* The type determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int)" +/* This argument is followed by an attribute name and an integer. +/* .IP "ATTR_TYPE_STR (char *, char *)" +/* This argument is followed by an attribute name and a null-terminated +/* string. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* The content of the hash table is sent as a sequence of string-valued +/* attributes with names equal to the hash table lookup key. +/* .IP ATTR_TYPE_END +/* This terminates the attribute list. +/* .RE +/* DIAGNOSTICS +/* The result value is 0 in case of success, VSTREAM_EOF in case +/* of trouble. +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_scan64(3) recover attributes from byte stream +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_print64_str - encode and send attribute information */ + +static void attr_print64_str(VSTREAM *fp, const char *str, int len) +{ + static VSTRING *base64_buf; + + if (base64_buf == 0) + base64_buf = vstring_alloc(10); + + base64_encode(base64_buf, str, len); + vstream_fputs(STR(base64_buf), fp); +} + +static void attr_print64_num(VSTREAM *fp, unsigned num) +{ + static VSTRING *plain; + + if (plain == 0) + plain = vstring_alloc(10); + + vstring_sprintf(plain, "%u", num); + attr_print64_str(fp, STR(plain), LEN(plain)); +} + +/* attr_vprint64 - send attribute list to stream */ + +int attr_vprint64(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_print64"; + int attr_type; + char *attr_name; + unsigned int_val; + char *str_val; + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Iterate over all (type, name, value) triples, and produce output on + * the fly. + */ + while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) { + switch (attr_type) { + case ATTR_TYPE_NUM: + attr_name = va_arg(ap, char *); + attr_print64_str(fp, attr_name, strlen(attr_name)); + int_val = va_arg(ap, int); + VSTREAM_PUTC(':', fp); + attr_print64_num(fp, (unsigned) int_val); + if (msg_verbose) + msg_info("send attr %s = %u", attr_name, int_val); + break; + case ATTR_TYPE_STR: + attr_name = va_arg(ap, char *); + attr_print64_str(fp, attr_name, strlen(attr_name)); + str_val = va_arg(ap, char *); + VSTREAM_PUTC(':', fp); + attr_print64_str(fp, str_val, strlen(str_val)); + if (msg_verbose) + msg_info("send attr %s = %s", attr_name, str_val); + break; + case ATTR_TYPE_HASH: + 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)); + VSTREAM_PUTC(':', fp); + attr_print64_str(fp, ht[0]->value, strlen(ht[0]->value)); + if (msg_verbose) + msg_info("send attr name %s value %s", + ht[0]->key, ht[0]->value); + if (ht[1] != 0) + VSTREAM_PUTC('\n', fp); + } + myfree((char *) ht_info_list); + break; + default: + msg_panic("%s: unknown type code: %d", myname, attr_type); + } + VSTREAM_PUTC('\n', fp); + } + if ((flags & ATTR_FLAG_MORE) == 0) + VSTREAM_PUTC('\n', fp); + return (vstream_ferror(fp)); +} + +int attr_print64(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vprint64(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan64 test + * program. + */ +#include + +int main(int unused_argc, char **argv) +{ + HTABLE *table = htable_create(1); + + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_verbose = 1; + htable_enter(table, "foo-name", mystrdup("foo-value")); + htable_enter(table, "bar-name", mystrdup("bar-value")); + attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_HASH, table, + ATTR_TYPE_END); + attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_END); + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + htable_free(table, myfree); + return (0); +} + +#endif diff --git a/postfix/src/util/attr_scan0.c b/postfix/src/util/attr_scan0.c new file mode 100644 index 000000000..85bab44b5 --- /dev/null +++ b/postfix/src/util/attr_scan0.c @@ -0,0 +1,407 @@ +/*++ +/* NAME +/* attr_scan0 3 +/* SUMMARY +/* recover attributes from byte stream +/* SYNOPSIS +/* #include +/* +/* int attr_scan0(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vscan0(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_scan0() takes zero or more (name, value) request attributes +/* and recovers the attribute values from the byte stream that was +/* possibly generated by attr_print0(). +/* +/* attr_vscan0() provides an alternative interface that is convenient +/* for calling from within a variadic function. +/* +/* The input stream is formatted as follows, where (item)* stands +/* for zero or more instances of the specified item, and where +/* (item1 | item2) stands for choice: +/* +/* .in +5 +/* attr-list :== simple-attr* null +/* .br +/* simple-attr :== attr-name null attr-value null +/* .br +/* attr-name :== any string not containing null +/* .br +/* attr-value :== any string not containing null +/* .br +/* null :== the ASCII null character +/* .in +/* +/* All attribute names and attribute values are sent as null terminated +/* strings. Each string must be no longer than 2*var_line_limit +/* characters. The formatting rules favor implementations in C. +/* +/* Normally, attributes must be received in the sequence as specified with +/* the attr_scan0() argument list. The input stream may contain additional +/* attributes at any point in the input stream, including additional +/* instances of requested attributes. +/* +/* Additional input attributes or input attribute instances are silently +/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified +/* (see below). This allows for some flexibility in the evolution of +/* protocols while still providing the option of being strict where +/* this is desirable. +/* +/* Arguments: +/* .IP fp +/* Stream to recover the input attributes from. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MISSING +/* Log a warning when the input attribute list terminates before all +/* requested attributes are recovered. It is always an error when the +/* input stream ends without the newline attribute list terminator. +/* .IP ATTR_FLAG_EXTRA +/* Log a warning and stop attribute recovery when the input stream +/* contains an attribute that was not requested. This includes the +/* case of additional instances of a requested attribute. +/* .IP ATTR_FLAG_MORE +/* After recovering the requested attributes, leave the input stream +/* in a state that is usable for more attr_scan0() operations from the +/* same input attribute list. +/* By default, attr_scan0() skips forward past the input attribute list +/* terminator. +/* .IP ATTR_FLAG_STRICT +/* For convenience, this value combines both ATTR_FLAG_MISSING and +/* ATTR_FLAG_EXTRA. +/* .IP ATTR_FLAG_NONE +/* For convenience, this value requests none of the above. +/* .RE +/* .IP type +/* The type argument determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int *)" +/* This argument is followed by an attribute name and an integer pointer. +/* .IP "ATTR_TYPE_STR (char *, VSTRING *)" +/* This argument is followed by an attribute name and a VSTRING pointer. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* 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. +/* .sp +/* The attribute string values are stored in the hash table under +/* keys equal to the attribute name (obtained from the input stream). +/* 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. +/* .IP ATTR_TYPE_END +/* This argument terminates the requested attribute list. +/* .RE +/* BUGS +/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly +/* untrusted sources. This is unsafe, unless the resulting table is +/* queried only with known to be good attribute names. +/* DIAGNOSTICS +/* attr_scan0() and attr_vscan0() return -1 when malformed input is +/* detected (string too long, incomplete line, missing end marker). +/* Otherwise, the result value is the number of attributes that were +/* successfully recovered from the input stream (a hash table counts +/* as the number of entries stored into the table). +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_print0(3) send attributes over byte stream. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Application specific. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_scan0_string - pull a string from the input stream */ + +static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context) +{ + extern int var_line_limit; /* XXX */ + int limit = var_line_limit * 2; + int ch; + + if ((ch = vstring_get_null_bound(plain_buf, fp, limit)) == VSTREAM_EOF) { + msg_warn("premature end-of-input from %s while reading %s", + VSTREAM_PATH(fp), context); + return (-1); + } + if (ch != 0) { + msg_warn("string length > %d characters from %s while reading %s", + limit, VSTREAM_PATH(fp), context); + return (-1); + } + if (msg_verbose) + msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)"); + return (ch); +} + +/* attr_scan0_number - pull a number from the input stream */ + +static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf, + const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan0_string(fp, str_buf, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_vscan0 - receive attribute list from stream */ + +int attr_vscan0(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_scan0"; + static VSTRING *str_buf = 0; + static VSTRING *name_buf = 0; + int wanted_type = -1; + char *wanted_name; + unsigned int *number; + VSTRING *string; + HTABLE *hash_table; + int ch; + int conversions; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Initialize. + */ + if (str_buf == 0) { + str_buf = vstring_alloc(10); + name_buf = vstring_alloc(10); + } + + /* + * Iterate over all (type, name, value) triples. + */ + for (conversions = 0; /* void */ ; conversions++) { + + /* + * Determine the next attribute type and attribute name on the + * caller's wish list. + * + * If we're reading into a hash table, we already know that the + * attribute value is string-valued, and we get the attribute name + * 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) { + wanted_type = va_arg(ap, int); + if (wanted_type == ATTR_TYPE_END) { + if ((flags & ATTR_FLAG_MORE) != 0) + return (conversions); + wanted_name = "(list terminator)"; + } 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 { + wanted_name = va_arg(ap, char *); + } + } + + /* + * Locate the next attribute of interest in the input stream. + */ + for (;;) { + + /* + * Get the name of the next attribute. Hitting EOF is always bad. + * Hitting the end-of-input early is OK if the caller is prepared + * to deal with missing inputs. + */ + if (msg_verbose) + msg_info("%s: wanted attribute: %s", + VSTREAM_PATH(fp), wanted_name); + if ((ch = attr_scan0_string(fp, name_buf, + "input attribute name")) == VSTREAM_EOF) + return (-1); + if (LEN(name_buf) == 0) { + if (wanted_type == ATTR_TYPE_END + || wanted_type == ATTR_TYPE_HASH) + return (conversions); + if ((flags & ATTR_FLAG_MISSING) != 0) + msg_warn("missing attribute %s in input from %s", + wanted_name, VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * See if the caller asks for this attribute. + */ + if (wanted_type == ATTR_TYPE_HASH + || (wanted_type != ATTR_TYPE_END + && strcmp(wanted_name, STR(name_buf)) == 0)) + break; + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("spurious attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * Skip over this attribute. The caller does not ask for it. + */ + (void) attr_scan0_string(fp, str_buf, "input attribute value"); + } + + /* + * Do the requested conversion. + */ + switch (wanted_type) { + case ATTR_TYPE_NUM: + number = va_arg(ap, unsigned int *); + if ((ch = attr_scan0_number(fp, number, str_buf, + "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_STR: + string = va_arg(ap, VSTRING *); + if ((ch = attr_scan0_string(fp, string, + "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_HASH: + if ((ch = attr_scan0_string(fp, str_buf, + "input attribute value")) < 0) + return (-1); + if (htable_locate(hash_table, STR(name_buf)) != 0) { + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("duplicate attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + } else { + htable_enter(hash_table, STR(name_buf), + mystrdup(STR(str_buf))); + } + break; + default: + msg_panic("%s: unknown type code: %d", myname, wanted_type); + } + } +} + +/* attr_scan0 - read attribute list from stream */ + +int attr_scan0(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vscan0(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan0 test + * program. + */ +#include + +int var_line_limit = 2048; + +int main(int unused_argc, char **used_argv) +{ + VSTRING *str_val = vstring_alloc(1); + HTABLE *table = htable_create(1); + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + int int_val; + int ret; + + msg_verbose = 1; + msg_vstream_init(used_argv[0], VSTREAM_ERR); + if ((ret = attr_scan0(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_HASH, table, + ATTR_TYPE_END)) > 2) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if ((ret = attr_scan0(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_END)) == 2) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + vstring_free(str_val); + htable_free(table, myfree); + + return (0); +} + +#endif diff --git a/postfix/src/util/attr_scan0.ref b/postfix/src/util/attr_scan0.ref new file mode 100644 index 000000000..bfd1bf970 --- /dev/null +++ b/postfix/src/util/attr_scan0.ref @@ -0,0 +1,36 @@ +./attr_print0: send attr number = 4711 +./attr_print0: send attr string = whoopee +./attr_print0: send attr name foo-name value foo-value +./attr_print0: send attr name bar-name value bar-value +./attr_print0: send attr number = 4711 +./attr_print0: send attr string = whoopee +./attr_scan0: unknown_stream: wanted attribute: number +./attr_scan0: input attribute name: number +./attr_scan0: input attribute value: 4711 +./attr_scan0: unknown_stream: wanted attribute: string +./attr_scan0: input attribute name: string +./attr_scan0: input attribute value: whoopee +./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator) +./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: 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: input attribute name: (end) +./attr_scan0: unknown_stream: wanted attribute: number +./attr_scan0: input attribute name: number +./attr_scan0: input attribute value: 4711 +./attr_scan0: unknown_stream: wanted attribute: string +./attr_scan0: input attribute name: string +./attr_scan0: input attribute value: whoopee +./attr_scan0: unknown_stream: wanted attribute: (list terminator) +./attr_scan0: input attribute name: (end) +number 4711 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value +number 4711 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value diff --git a/postfix/src/util/attr_scan64.c b/postfix/src/util/attr_scan64.c new file mode 100644 index 000000000..8ad10847b --- /dev/null +++ b/postfix/src/util/attr_scan64.c @@ -0,0 +1,460 @@ +/*++ +/* NAME +/* attr_scan64 3 +/* SUMMARY +/* recover attributes from byte stream +/* SYNOPSIS +/* #include +/* +/* int attr_scan64(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vscan64(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_scan64() takes zero or more (name, value) request attributes +/* and recovers the attribute values from the byte stream that was +/* possibly generated by attr_print64(). +/* +/* attr_vscan64() provides an alternative interface that is convenient +/* for calling from within a variadic function. +/* +/* The input stream is formatted as follows, where (item)* stands +/* for zero or more instances of the specified item, and where +/* (item1 | item2) stands for choice: +/* +/* .in +5 +/* attr-list :== simple-attr* newline +/* .br +/* simple-attr :== attr-name colon attr-value newline +/* .br +/* attr-name :== any base64 encoded string +/* .br +/* attr-value :== any base64 encoded string +/* .br +/* colon :== the ASCII colon character +/* .br +/* newline :== the ASCII newline character +/* .in +/* +/* All attribute names and attribute values are sent as base64-encoded +/* strings. Each base64 encoding must be no longer than 2*var_line_limit +/* characters. The formatting rules aim to make implementations in PERL +/* and other languages easy. +/* +/* Normally, attributes must be received in the sequence as specified with +/* the attr_scan64() argument list. The input stream may contain additional +/* attributes at any point in the input stream, including additional +/* instances of requested attributes. +/* +/* Additional input attributes or input attribute instances are silently +/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified +/* (see below). This allows for some flexibility in the evolution of +/* protocols while still providing the option of being strict where +/* this is desirable. +/* +/* Arguments: +/* .IP fp +/* Stream to recover the input attributes from. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MISSING +/* Log a warning when the input attribute list terminates before all +/* requested attributes are recovered. It is always an error when the +/* input stream ends without the newline attribute list terminator. +/* .IP ATTR_FLAG_EXTRA +/* Log a warning and stop attribute recovery when the input stream +/* contains an attribute that was not requested. This includes the +/* case of additional instances of a requested attribute. +/* .IP ATTR_FLAG_MORE +/* After recovering the requested attributes, leave the input stream +/* in a state that is usable for more attr_scan64() operations from the +/* same input attribute list. +/* By default, attr_scan64() skips forward past the input attribute list +/* terminator. +/* .IP ATTR_FLAG_STRICT +/* For convenience, this value combines both ATTR_FLAG_MISSING and +/* ATTR_FLAG_EXTRA. +/* .IP ATTR_FLAG_NONE +/* For convenience, this value requests none of the above. +/* .RE +/* .IP type +/* The type argument determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int *)" +/* This argument is followed by an attribute name and an integer pointer. +/* .IP "ATTR_TYPE_STR (char *, VSTRING *)" +/* This argument is followed by an attribute name and a VSTRING pointer. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* 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. +/* .sp +/* The attribute string values are stored in the hash table under +/* keys equal to the attribute name (obtained from the input stream). +/* 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. +/* .IP ATTR_TYPE_END +/* This argument terminates the requested attribute list. +/* .RE +/* BUGS +/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly +/* untrusted sources. This is unsafe, unless the resulting table is +/* queried only with known to be good attribute names. +/* DIAGNOSTICS +/* attr_scan64() and attr_vscan64() return -1 when malformed input is +/* detected (string too long, incomplete line, missing end marker). +/* Otherwise, the result value is the number of attributes that were +/* successfully recovered from the input stream (a hash table counts +/* as the number of entries stored into the table). +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_print64(3) send attributes over byte stream. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Application specific. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_scan64_string - pull a string from the input stream */ + +static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *context) +{ + static VSTRING *base64_buf = 0; + extern int var_line_limit; /* XXX */ + int limit = var_line_limit * 2; + int ch; + + if (base64_buf == 0) + base64_buf = vstring_alloc(10); + + VSTRING_RESET(base64_buf); + while ((ch = VSTREAM_GETC(fp)) != ':' && ch != '\n') { + if (ch == VSTREAM_EOF) { + msg_warn("premature end-of-input from %s while reading %s", + VSTREAM_PATH(fp), context); + return (-1); + } + VSTRING_ADDCH(base64_buf, ch); + if (LEN(base64_buf) > limit) { + msg_warn("string length > %d characters from %s while reading %s", + limit, VSTREAM_PATH(fp), context); + return (-1); + } + } + VSTRING_TERMINATE(base64_buf); + if (base64_decode(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) { + msg_warn("malformed base64 data from %s: %.100s", + VSTREAM_PATH(fp), STR(base64_buf)); + return (-1); + } + if (msg_verbose) + msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)"); + return (ch); +} + +/* attr_scan64_number - pull a number from the input stream */ + +static int attr_scan64_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf, + const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan64_string(fp, str_buf, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_vscan64 - receive attribute list from stream */ + +int attr_vscan64(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_scan64"; + static VSTRING *str_buf = 0; + static VSTRING *name_buf = 0; + int wanted_type = -1; + char *wanted_name; + unsigned int *number; + VSTRING *string; + HTABLE *hash_table; + int ch; + int conversions; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Initialize. + */ + if (str_buf == 0) { + str_buf = vstring_alloc(10); + name_buf = vstring_alloc(10); + } + + /* + * Iterate over all (type, name, value) triples. + */ + for (conversions = 0; /* void */ ; conversions++) { + + /* + * Determine the next attribute type and attribute name on the + * caller's wish list. + * + * If we're reading into a hash table, we already know that the + * attribute value is string-valued, and we get the attribute name + * 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) { + wanted_type = va_arg(ap, int); + if (wanted_type == ATTR_TYPE_END) { + if ((flags & ATTR_FLAG_MORE) != 0) + return (conversions); + wanted_name = "(list terminator)"; + } 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 { + wanted_name = va_arg(ap, char *); + } + } + + /* + * Locate the next attribute of interest in the input stream. + */ + for (;;) { + + /* + * Get the name of the next attribute. Hitting EOF is always bad. + * Hitting the end-of-input early is OK if the caller is prepared + * to deal with missing inputs. + */ + if (msg_verbose) + msg_info("%s: wanted attribute: %s", + VSTREAM_PATH(fp), wanted_name); + if ((ch = attr_scan64_string(fp, name_buf, + "input attribute name")) == VSTREAM_EOF) + return (-1); + if (ch == '\n' && LEN(name_buf) == 0) { + if (wanted_type == ATTR_TYPE_END + || wanted_type == ATTR_TYPE_HASH) + return (conversions); + if ((flags & ATTR_FLAG_MISSING) != 0) + msg_warn("missing attribute %s in input from %s", + wanted_name, VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * See if the caller asks for this attribute. + */ + if (wanted_type == ATTR_TYPE_HASH + || (wanted_type != ATTR_TYPE_END + && strcmp(wanted_name, STR(name_buf)) == 0)) + break; + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("spurious attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * Skip over this attribute. The caller does not ask for it. + */ + while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n') + /* void */ ; + } + + /* + * Do the requested conversion. If the target attribute is a + * non-array type, disallow sending a multi-valued attribute, and + * disallow sending no value. If the target attribute is an array + * type, allow the sender to send a zero-element array (i.e. no value + * at all). XXX Need to impose a bound on the number of array + * elements. + */ + switch (wanted_type) { + case ATTR_TYPE_NUM: + if (ch != ':') { + msg_warn("missing value for number attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + number = va_arg(ap, unsigned int *); + if ((ch = attr_scan64_number(fp, number, str_buf, + "input attribute value")) < 0) + return (-1); + if (ch != '\n') { + msg_warn("multiple values for attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + break; + case ATTR_TYPE_STR: + if (ch != ':') { + msg_warn("missing value for string attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + string = va_arg(ap, VSTRING *); + if ((ch = attr_scan64_string(fp, string, + "input attribute value")) < 0) + return (-1); + if (ch != '\n') { + msg_warn("multiple values for attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + break; + case ATTR_TYPE_HASH: + if (ch != ':') { + msg_warn("missing value for string attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + if ((ch = attr_scan64_string(fp, str_buf, + "input attribute value")) < 0) + return (-1); + if (ch != '\n') { + msg_warn("multiple values for attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + if (htable_locate(hash_table, STR(name_buf)) != 0) { + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("duplicate attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + } else { + htable_enter(hash_table, STR(name_buf), + mystrdup(STR(str_buf))); + } + break; + default: + msg_panic("%s: unknown type code: %d", myname, wanted_type); + } + } +} + +/* attr_scan64 - read attribute list from stream */ + +int attr_scan64(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vscan64(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan64 test + * program. + */ +#include + +int var_line_limit = 2048; + +int main(int unused_argc, char **used_argv) +{ + VSTRING *str_val = vstring_alloc(1); + HTABLE *table = htable_create(1); + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + int int_val; + int ret; + + msg_verbose = 1; + msg_vstream_init(used_argv[0], VSTREAM_ERR); + if ((ret = attr_scan64(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_HASH, table, + ATTR_TYPE_END)) > 2) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if ((ret = attr_scan64(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_END)) == 2) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + vstring_free(str_val); + htable_free(table, myfree); + + return (0); +} + +#endif diff --git a/postfix/src/util/attr_scan64.ref b/postfix/src/util/attr_scan64.ref new file mode 100644 index 000000000..7d494be79 --- /dev/null +++ b/postfix/src/util/attr_scan64.ref @@ -0,0 +1,36 @@ +./attr_print64: send attr number = 4711 +./attr_print64: send attr string = whoopee +./attr_print64: send attr name foo-name value foo-value +./attr_print64: send attr name bar-name value bar-value +./attr_print64: send attr number = 4711 +./attr_print64: send attr string = whoopee +./attr_scan64: unknown_stream: wanted attribute: number +./attr_scan64: input attribute name: number +./attr_scan64: input attribute value: 4711 +./attr_scan64: unknown_stream: wanted attribute: string +./attr_scan64: input attribute name: string +./attr_scan64: input attribute value: whoopee +./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator) +./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: 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: input attribute name: (end) +./attr_scan64: unknown_stream: wanted attribute: number +./attr_scan64: input attribute name: number +./attr_scan64: input attribute value: 4711 +./attr_scan64: unknown_stream: wanted attribute: string +./attr_scan64: input attribute name: string +./attr_scan64: input attribute value: whoopee +./attr_scan64: unknown_stream: wanted attribute: (list terminator) +./attr_scan64: input attribute name: (end) +number 4711 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value +number 4711 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value diff --git a/postfix/src/util/iostuff.h b/postfix/src/util/iostuff.h index 7da0f297d..ac9681b1d 100644 --- a/postfix/src/util/iostuff.h +++ b/postfix/src/util/iostuff.h @@ -29,6 +29,9 @@ extern int timed_write(int, void *, unsigned, int, void *); extern void doze(unsigned); extern void rand_sleep(unsigned, unsigned); extern int duplex_pipe(int *); +extern int sock_empty_wait(int, int); +extern int sock_maximize_send_lowat(int); +extern void sock_set_send_lowat(int, int); #define BLOCKING 0 #define NON_BLOCKING 1 diff --git a/postfix/src/util/match_list.c b/postfix/src/util/match_list.c index 7ae42d6d5..993dc324f 100644 --- a/postfix/src/util/match_list.c +++ b/postfix/src/util/match_list.c @@ -6,10 +6,11 @@ /* SYNOPSIS /* #include /* -/* MATCH_LIST *match_list_init(pattern_list, count, func,...) +/* MATCH_LIST *match_list_init(flags, pattern_list, count, func,...) +/* int flags; /* const char *pattern_list; /* int count; -/* int (*func)(const char *string, const char *pattern); +/* int (*func)(int flags, const char *string, const char *pattern); /* /* int match_list_match(list, string,...) /* MATCH_LIST *list; @@ -28,9 +29,19 @@ /* a pattern match, precede a non-file name pattern with an /* exclamation point (!). /* -/* match_list_init() performs initializations. The first argument is -/* a list of patterns. The second argument specifies how many match -/* functions follow. +/* match_list_init() performs initializations. The flags argument +/* specifies the bit-wise OR of zero or more of the following: +/* .RS +/* .IP MATCH_FLAG_PARENT +/* The hostname pattern foo.com matches any name within the domain +/* foo.com. +/* .IP MATCH_FLAG_DOTPARENT +/* The hostname pattern .foo.com matches any name under foo.com. +/* The pattern foo.com matches itself only. +/* .RE +/* Specify MATCH_FLAG_NONE to request none of the above. +/* The pattern_list argument specifies a list of patterns. The third +/* argument specifies how many match functions follow. /* /* match_list_match() matches strings against the specified pattern /* list, passing the first string to the first function given to @@ -73,11 +84,13 @@ #include #include #include +#include #include /* Application-specific */ struct MATCH_LIST { + int flags; /* processing options */ ARGV *patterns; /* one pattern each */ int match_count; /* match function/argument count */ MATCH_LIST_FN *match_func; /* match functions */ @@ -125,14 +138,18 @@ static ARGV *match_list_parse(ARGV *list, char *string) /* match_list_init - initialize pattern list */ -MATCH_LIST *match_list_init(const char *patterns, int match_count,...) +MATCH_LIST *match_list_init(int flags, const char *patterns, int match_count,...) { MATCH_LIST *list; char *saved_patterns; va_list ap; int i; + if (flags & ~MATCH_FLAG_ALL) + msg_panic("match_list_init: bad flags 0x%x", flags); + list = (MATCH_LIST *) mymalloc(sizeof(*list)); + list->flags = flags; list->match_count = match_count; list->match_func = (MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN)); @@ -173,7 +190,7 @@ int match_list_match(MATCH_LIST * list,...) for (match = 1; *pat == '!'; pat++) match = !match; for (i = 0; i < list->match_count; i++) - if (list->match_func[i] (list->match_args[i], pat) != 0) + if (list->match_func[i] (list->flags, list->match_args[i], pat)) return (match); } if (msg_verbose) diff --git a/postfix/src/util/match_list.h b/postfix/src/util/match_list.h index aba88b8f6..910ae713b 100644 --- a/postfix/src/util/match_list.h +++ b/postfix/src/util/match_list.h @@ -15,9 +15,9 @@ * External interface. */ typedef struct MATCH_LIST MATCH_LIST; -typedef int (*MATCH_LIST_FN) (const char *, const char *); +typedef int (*MATCH_LIST_FN) (int, const char *, const char *); -extern MATCH_LIST *match_list_init(const char *, int,...); +extern MATCH_LIST *match_list_init(int, const char *, int,...); extern int match_list_match(MATCH_LIST *,...); extern void match_list_free(MATCH_LIST *); diff --git a/postfix/src/util/match_ops.c b/postfix/src/util/match_ops.c index 764866bbc..4f018d2cb 100644 --- a/postfix/src/util/match_ops.c +++ b/postfix/src/util/match_ops.c @@ -6,15 +6,18 @@ /* SYNOPSIS /* #include /* -/* int match_string(string, pattern) +/* int match_string(flags, string, pattern) +/* int flags; /* const char *string; /* const char *pattern; /* -/* int match_hostname(name, pattern) +/* int match_hostname(flags, name, pattern) +/* int flags; /* const char *name; /* const char *pattern; /* -/* int match_hostaddr(addr, pattern) +/* int match_hostaddr(flags, addr, pattern) +/* int flags; /* const char *addr; /* const char *pattern; /* DESCRIPTION @@ -24,16 +27,25 @@ /* or address comparison. /* /* match_string() matches the string against the pattern, requiring -/* an exact (case-insensitive) match. +/* an exact (case-insensitive) match. The flags argument is not used. /* /* match_hostname() matches the host name when the hostname matches /* the pattern exactly, or when the pattern matches a parent domain -/* of the named host. +/* of the named host. The flags argument specifies the bit-wise OR +/* of zero or more of the following: +/* .IP MATCH_FLAG_PARENT +/* The pattern foo.com matches any name within the domain foo.com. +/* .IP MATCH_FLAG_DOTPARENT +/* The pattern .foo.com matches any name under foo.com. The pattern +/* foo.com matches itself only. +/* .RE +/* Specify MATCH_FLAG_NONE to request none of the above. /* /* match_hostaddr() matches a host address when the pattern is /* identical to the host address, or when the pattern is a net/mask /* that contains the address. The mask specifies the number of -/* bits in the network part of the pattern. +/* bits in the network part of the pattern. The flags argument is +/* not used. /* LICENSE /* .ad /* .fi @@ -72,7 +84,7 @@ /* match_string - match a string literal */ -int match_string(const char *string, const char *pattern) +int match_string(int unused_flags, const char *string, const char *pattern) { char *myname = "match_string"; int match; @@ -110,7 +122,7 @@ int match_string(const char *string, const char *pattern) /* match_hostname - match a host by name */ -int match_hostname(const char *name, const char *pattern) +int match_hostname(int flags, const char *name, const char *pattern) { char *myname = "match_hostname"; const char *pd; @@ -128,13 +140,25 @@ int match_hostname(const char *name, const char *pattern) if (strchr(pattern, ':') != 0) { temp = lowercase(mystrdup(name)); match = 0; - for (entry = temp; /* void */ ; entry = next + 1) { - if ((match = (dict_lookup(pattern, entry) != 0)) != 0) - break; - if (dict_errno != 0) - msg_fatal("%s: table lookup problem", pattern); - if ((next = strchr(entry, '.')) == 0) - break; + if (flags & MATCH_FLAG_PARENT) { + for (entry = temp; /* void */ ; entry = next + 1) { + if ((match = (dict_lookup(pattern, entry) != 0)) != 0) + break; + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", pattern); + if ((next = strchr(entry, '.')) == 0) + break; + } + } + if (flags & MATCH_FLAG_DOTPARENT) { + for (entry = temp; /* void */ ; entry = next) { + if ((match = (dict_lookup(pattern, entry) != 0)) != 0) + break; + if (dict_errno != 0) + msg_fatal("%s: table lookup problem", pattern); + if ((next = strchr(entry, '.')) == 0) + break; + } } myfree(temp); return (match); @@ -151,9 +175,16 @@ int match_hostname(const char *name, const char *pattern) * See if the pattern is a parent domain of the hostname. */ else { - pd = name + strlen(name) - strlen(pattern); - if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0) - return (1); + if (flags & MATCH_FLAG_PARENT) { + pd = name + strlen(name) - strlen(pattern); + if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0) + return (1); + } + if (flags & MATCH_FLAG_DOTPARENT) { + pd = name + strlen(name) - strlen(pattern); + if (pd > name && pd[-1] == '.' && strcasecmp(pd - 1, pattern) == 0) + return (1); + } } return (0); } @@ -181,7 +212,7 @@ static int match_parse_mask(const char *pattern, unsigned long *net_bits, /* match_hostaddr - match host by address */ -int match_hostaddr(const char *addr, const char *pattern) +int match_hostaddr(int unused_flags, const char *addr, const char *pattern) { char *myname = "match_hostaddr"; int mask_shift; diff --git a/postfix/src/util/match_ops.h b/postfix/src/util/match_ops.h index a938c04fb..9a66c74ef 100644 --- a/postfix/src/util/match_ops.h +++ b/postfix/src/util/match_ops.h @@ -13,9 +13,14 @@ /* External interface. */ -extern int match_string(const char *, const char *); -extern int match_hostname(const char *, const char *); -extern int match_hostaddr(const char *, const char *); +#define MATCH_FLAG_NONE 0 +#define MATCH_FLAG_PARENT (1<<0) +#define MATCH_FLAG_DOTPARENT (1<<1) +#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT|MATCH_FLAG_DOTPARENT) + +extern int match_string(int, const char *, const char *); +extern int match_hostname(int, const char *, const char *); +extern int match_hostaddr(int, const char *, const char *); /* LICENSE /* .ad diff --git a/postfix/src/util/sock_empty_wait.c b/postfix/src/util/sock_empty_wait.c new file mode 100644 index 000000000..8cc681992 --- /dev/null +++ b/postfix/src/util/sock_empty_wait.c @@ -0,0 +1,207 @@ +/*++ +/* NAME +/* sock_empty_wait 3 +/* SUMMARY +/* wait until socket send buffer is near empty +/* SYNOPSIS +/* #include +/* +/* int sock_empty_wait(fd, timeout) +/* int fd; +/* int timeout; +/* AUXILIARY ROUTINES +/* int sock_maximize_send_lowat(fd) +/* int fd; +/* +/* void sock_set_send_lowat(fd, send_lowat) +/* int fd; +/* int send_lowat; +/* DESCRIPTION +/* sock_empty_wait() blocks the process until the specified socket's +/* send buffer is near empty, in the hope that the contents of the +/* next write() operation will not be merged with preceding data. +/* +/* sock_maximize_send_lowat() maximizes the socket send buffer +/* low-water mark, which controls how much free buffer space must +/* be available before the socket is considered writable. +/* The result value is the old low-water mark value. +/* +/* sock_set_send_lowat() sets the socket send buffer low-water mark +/* to the specified value. +/* +/* Arguments: +/* .IP fd +/* File descriptor in the range 0..FD_SETSIZE. +/* .IP timeout +/* If positive, deadline in seconds. A zero value effects a poll. +/* A negative value means wait until something happens. +/* DIAGNOSTICS +/* sock_maximize_send_lowat() returns -1 if the kernel does not +/* support access to or control over the send buffer low-water mark. +/* Otherwise, all system call errors are fatal. +/* +/* A zero result means success. When the specified deadline is +/* exceeded, sock_empty_wait() returns -1 and sets errno to ETIMEDOUT. +/* BUGS +/* Linux and Solaris do not provide the necessary support. Until +/* they become better citizens they are punished by having to sleep +/* for 10 seconds, and even then there is no reasonable way to find +/* out if all the written data was acknowledged by the remote TCP. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include + +/* sock_maximize_send_lowat - maximize the send buffer low-water mark */ + +int sock_maximize_send_lowat(int fd) +{ + char *myname = "sock_maximize_send_lowat"; + int send_buffer_size; + int saved_low_water_mark; + int want_low_water_mark; + int got_low_water_mark; + SOCKOPT_SIZE optlen; + + /* + * Get the send buffer size. If this succeeds then we know the file + * handle is a socket. + */ + optlen = sizeof(send_buffer_size); + if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, + (char *) &send_buffer_size, &optlen) < 0) + msg_fatal("%s: getsockopt SO_SNDBUF: %m", myname); + + /* + * Save the send buffer low-water mark. XXX Solaris 8 does not support + * this operation. + */ + optlen = sizeof(saved_low_water_mark); + if (getsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, + (char *) &saved_low_water_mark, &optlen) < 0) { + if (msg_verbose) + msg_info("%s: getsockopt SO_SNDLOWAT: %m", myname); + return (-1); + } + + /* + * Max out the send buffer low-water mark. XXX Linux 2.4.14 does not + * support this operation. + */ + want_low_water_mark = send_buffer_size; + if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, + (char *) &want_low_water_mark, + sizeof(want_low_water_mark)) < 0) { + if (msg_verbose) + msg_info("%s: setsockopt SO_SNDLOWAT: %m", myname); + return (-1); + } + + /* + * Make sure the kernel did not cheat. + */ + optlen = sizeof(got_low_water_mark); + if (getsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, + (char *) &got_low_water_mark, &optlen) < 0) + msg_fatal("%s: getsockopt SO_SNDLOWAT: %m", myname); + if (got_low_water_mark == 1) + return (-1); + + /* + * Make debugging a bit easier. + */ + if (msg_verbose) { + msg_info("%s: send buffer %d, low-water mark was %d, wanted %d, got %d", + myname, send_buffer_size, saved_low_water_mark, + want_low_water_mark, got_low_water_mark); + + } + return (saved_low_water_mark); +} + +/* sock_set_send_lowat - restore socket send buffer low-water mark */ + +void sock_set_send_lowat(int fd, int want_low_water_mark) +{ + char *myname = "sock_set_send_lowat"; + int got_low_water_mark; + SOCKOPT_SIZE optlen; + + /* + * Set the send buffer low-water mark. + */ + if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, + (char *) &want_low_water_mark, + sizeof(want_low_water_mark)) < 0) + msg_fatal("%s: setsockopt SO_SNDLOWAT: %m", myname); + + /* + * Make debugging a bit easier. + */ + if (msg_verbose) { + optlen = sizeof(got_low_water_mark); + if (getsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, + (char *) &got_low_water_mark, &optlen) < 0) + msg_fatal("%s: getsockopt SO_SNDLOWAT: %m", myname); + msg_info("%s: low-water mark wanted %d, got %d", + myname, want_low_water_mark, got_low_water_mark); + } +} + +/* sock_empty_wait - wait until socket send buffer is near empty */ + +int sock_empty_wait(int fd, int timeout) +{ + int saved_errno; + int saved_low_water_mark; + int result; + + /* + * Max out the send buffer low-water mark. + */ + saved_low_water_mark = sock_maximize_send_lowat(fd); + + /* + * Wait until the socket is considered writable. + */ + result = write_wait(fd, timeout); + + /* + * Work around systems that have no functional SO_SNDLOWAT control. + */ + if (result == 0 && saved_low_water_mark <= 0) + sleep(10); + + /* + * Restore the send buffer low-water mark. + */ + if (saved_low_water_mark > 0) { + saved_errno = errno; + sock_set_send_lowat(fd, saved_low_water_mark); + errno = saved_errno; + } + + /* + * Done. + */ + return (result); +} diff --git a/postfix/src/util/vstring_vstream.c b/postfix/src/util/vstring_vstream.c index 078d255e1..2f644bf1d 100644 --- a/postfix/src/util/vstring_vstream.c +++ b/postfix/src/util/vstring_vstream.c @@ -27,6 +27,11 @@ /* VSTRING *vp; /* VSTREAM *fp; /* int bound; +/* +/* int vstring_get_null_bound(vp, fp, bound) +/* VSTRING *vp; +/* VSTREAM *fp; +/* int bound; /* DESCRIPTION /* The routines in this module each read one newline or null-terminated /* string from an input stream. In all cases the result is either the @@ -41,7 +46,7 @@ /* vstring_get_null() reads a null-terminated string from the named /* stream. /* -/* vstring_get_bound() and vstring_get_nonl_bound() read no more +/* the vstring_get_bound() routines read no more /* than \fIbound\fR characters. Otherwise they behave like the /* unbounded versions documented above. /* DIAGNOSTICS @@ -154,6 +159,22 @@ int vstring_get_nonl_bound(VSTRING *vp, VSTREAM *fp, int bound) return (c == '\n' ? c : VSTRING_GET_RESULT(vp)); } +/* vstring_get_null_bound - read null-terminated string from file */ + +int vstring_get_null_bound(VSTRING *vp, VSTREAM *fp, int bound) +{ + int c; + + if (bound <= 0) + msg_panic("vstring_get_nonl_bound: invalid bound %d", bound); + + VSTRING_RESET(vp); + while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0) + VSTRING_ADDCH(vp, c); + VSTRING_TERMINATE(vp); + return (c == 0 ? c : VSTRING_GET_RESULT(vp)); +} + #ifdef TEST /* diff --git a/postfix/src/util/vstring_vstream.h b/postfix/src/util/vstring_vstream.h index 897167ca6..68bd8106d 100644 --- a/postfix/src/util/vstring_vstream.h +++ b/postfix/src/util/vstring_vstream.h @@ -24,6 +24,7 @@ extern int vstring_get_nonl(VSTRING *, VSTREAM *); extern int vstring_get_null(VSTRING *, VSTREAM *); extern int vstring_get_bound(VSTRING *, VSTREAM *, int); extern int vstring_get_nonl_bound(VSTRING *, VSTREAM *, int); +extern int vstring_get_null_bound(VSTRING *, VSTREAM *, int); /* * Backwards compatibility for code that still uses the vstring_fgets() diff --git a/postfix/src/virtual/virtual.c b/postfix/src/virtual/virtual.c index 14c388e8a..7968dcc28 100644 --- a/postfix/src/virtual/virtual.c +++ b/postfix/src/virtual/virtual.c @@ -70,9 +70,11 @@ /* numerical user ID values that may be specified in any /* \fBvirtual_owner_maps\fR or \fBvirtual_uid_maps\fR. /* SECURITY +/* .ad +/* .fi /* The virtual delivery agent is not security sensitive, provided -/* that the lookup tables with recipient information are adequately -/* protected. This program is not designed to run chrooted. +/* that the lookup tables with recipient user/group ID information are +/* adequately protected. This program is not designed to run chrooted. /* STANDARDS /* RFC 822 (ARPA Internet Text Messages) /* DIAGNOSTICS @@ -138,7 +140,7 @@ /* This setting is ignored with \fBmaildir\fR style delivery, /* because such deliveries are safe without explicit locks. /* -/* Use the command \fBpostconf -m\fR to find out what locking methods +/* Use the command \fBpostconf -l\fR to find out what locking methods /* are available on your system. /* .IP \fBdeliver_lock_attempts\fR /* Limit the number of attempts to acquire an exclusive lock