From 0e961e79a87572a6656ccf4e9d8c494e0829dcac Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Wed, 23 Aug 2006 00:00:00 -0500 Subject: [PATCH] postfix-2.4-20060823 --- postfix/HISTORY | 67 ++- postfix/README_FILES/CDB_README | 5 +- postfix/README_FILES/SASL_README | 15 +- .../auxiliary/name-addr-test/getaddrinfo.c | 64 +++ .../auxiliary/name-addr-test/gethostbyaddr.c | 46 ++ .../auxiliary/name-addr-test/gethostbyname.c | 44 ++ .../auxiliary/name-addr-test/getnameinfo.c | 79 +++ postfix/conf/post-install | 1 + postfix/html/CDB_README.html | 5 +- postfix/html/SASL_README.html | 9 +- postfix/html/master.5.html | 4 +- postfix/html/postconf.5.html | 8 +- postfix/html/qmqp-sink.1.html | 6 + postfix/html/qmqp-source.1.html | 6 +- postfix/html/smtp-sink.1.html | 124 ++++- postfix/html/smtp-source.1.html | 4 + postfix/man/man1/qmqp-sink.1 | 5 + postfix/man/man1/qmqp-source.1 | 5 +- postfix/man/man1/smtp-sink.1 | 98 ++++ postfix/man/man1/smtp-source.1 | 3 + postfix/man/man5/master.5 | 2 +- postfix/man/man5/postconf.5 | 7 +- postfix/proto/CDB_README.html | 5 +- postfix/proto/SASL_README.html | 9 +- postfix/proto/master | 2 +- postfix/proto/postconf.proto | 8 +- postfix/src/cleanup/cleanup_milter.c | 7 +- postfix/src/global/mail_date.c | 2 +- postfix/src/global/mail_version.h | 2 +- postfix/src/smtpd/smtpd.c | 8 +- postfix/src/smtpstone/qmqp-sink.c | 5 + postfix/src/smtpstone/qmqp-source.c | 5 +- postfix/src/smtpstone/smtp-sink.c | 494 +++++++++++++++++- postfix/src/smtpstone/smtp-source.c | 10 +- postfix/src/util/peekfd.c | 4 + postfix/src/util/vstream.c | 19 +- 36 files changed, 1104 insertions(+), 83 deletions(-) create mode 100644 postfix/auxiliary/name-addr-test/getaddrinfo.c create mode 100644 postfix/auxiliary/name-addr-test/gethostbyaddr.c create mode 100644 postfix/auxiliary/name-addr-test/gethostbyname.c create mode 100644 postfix/auxiliary/name-addr-test/getnameinfo.c diff --git a/postfix/HISTORY b/postfix/HISTORY index 5856ca152..100cb1ec3 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -12628,9 +12628,10 @@ Apologies for any names omitted. limit in the SMTP server didn't work for size limits close enough to INT_MAX. File: smtpd/smtpd.c. - Bugfix: after an SMTP client was rejected with "smtpd_delay_reject - = no", the SMTP server would panic as it generated spurious - Milter requests for unrecognized commands. File: smtpd/smtpd.c. + Bugfix (introduced Postfix 2.3): after an SMTP client was + rejected with "smtpd_delay_reject = no", the SMTP server + would panic as it generated spurious Milter requests for + unrecognized commands. File: smtpd/smtpd.c. 20060727 @@ -12640,8 +12641,9 @@ Apologies for any names omitted. 20060805 - Bugfix: #ifdef damage caused smtp_sasl_start() to be invoked - twice. Reported by C-J Lofstedt. File: smtp/smtp_sasl_proto.c. + Bugfix (introduced Postfix 2.3): #ifdef damage caused + smtp_sasl_start() to be invoked twice. Reported by C-J + Lofstedt. File: smtp/smtp_sasl_proto.c. 20060806 @@ -12650,8 +12652,63 @@ Apologies for any names omitted. helpdesk service that solves all their email problems. Credits to Jonathan Balester. File: bounce/bounce_templates.c. +20060807 + + Bugfix (introduced Postfix 2.2): when upgrading from Postfix + < 2.2 with the third-party TLS patch, the post-install + upgrade procedure didn't put a "?" in the existing tlsmgr + entry, causing tlsmgr to repeatedly start and exit when TLS + support was not compiled in. File: conf/post-install. + +20060812 + + Bugfix (introduced < Postfix alpha): safety mechanism in + mail_date() didn't work. Found in code review. File: + global/mail_date.c. + +20060817 + + Test programs for host address->name and name->address + lookups to debug name service inconsistencies, typically + when the Postfix SMTP server claims that a hostname is + "unknown". Files: auxiliary/name-addr-test/*. + +20060822 + + Added missing logging for "message to large" etc. Files: + smtpd/smtpd.c, cleanup/cleanup_milter.c. + +20060823 + + Bugfix (introduced Postfix 2.2): vstream_fdclose() did not + flush unwritten output before disconnecting a stream from + its file descriptor. File: util/vstream.c. + + Bugfix (introduced Postfix 2.2): segfault when vstream_fclose() + attempted to flush unwritten output, after vstream_fdclose() + disconnected the stream from its file descriptor. File: + util/vstream.c. + + Feature: smtp-sink can capture mail to file, either as one + individual message per file, or as multiple messages per + file. After an initial implementation by Weidong Cui. File: + smtpstone/smtp-sink.c. + + Bugfix (introduced < Postfix alpha): smtp-sink did not + correctly recognize DOT-CR-LF immediately after DATA. File: + smtpstone/smtp-sink.c. + + Cleanup: smtp-sink now requires that MAIL FROM, RCPT TO and + DATA be send in the correct order. This simplified the + implementation of the capture to file feature. File: + smtpstone/smtp-sink.c. + Wish list: + Make null local-part handling configurable: either expand + into mailer-daemon (current bahavior) or disallow (strict + behavior, currently implemented only in the SMTP server). + The type of var_message_limit should be changed from int to long or better, to take advantage of LP64 architectures. This also requires checking all expressions in which diff --git a/postfix/README_FILES/CDB_README b/postfix/README_FILES/CDB_README index 0d00bf59a..b93993291 100644 --- a/postfix/README_FILES/CDB_README +++ b/postfix/README_FILES/CDB_README @@ -49,10 +49,11 @@ Alternatively, for the D.J.B. version of CDB: "AUXLIBS=$CDB/cdb.a $CDB/alloc.a $CDB/buffer.a $CDB/unix.a $CDB/byte.a" % make -After postfix has been built with cdb support, you can use "cdb" tables +After Postfix has been built with cdb support, you can use "cdb" tables wherever you can use read-only "hash", "btree" or "dbm" tables. However, the "ppoossttmmaapp --ii" (incremental record insertion) and "ppoossttmmaapp --dd" (incremental record deletion) command-line options are not available. For the same reason the "cdb" map type cannot be used to store the persistent address verification -cache for the verify(8) service. +cache for the verify(8) service, or to store TLS session information for the +tlsmgr(8) service. diff --git a/postfix/README_FILES/SASL_README b/postfix/README_FILES/SASL_README index 87ae46b27..5cf940db4 100644 --- a/postfix/README_FILES/SASL_README +++ b/postfix/README_FILES/SASL_README @@ -61,24 +61,23 @@ Needless to say, these commands are not available in earlier Postfix versions. BBuuiillddiinngg PPoossttffiixx wwiitthh DDoovveeccoott SSAASSLL ssuuppppoorrtt -Dovecot SASL support is available in Postfix 2.3 and later. The Dovecot source -code is available via http://www.dovecot.org/. At the time of writing, only -server-side SASL support is available, so you can't use it to authenticate to -your network provider's server. Dovecot uses its own daemon process for -authentication. This keeps the Postfix build process simple, because there is -no need to link extra libraries into Postfix. +Support for the Dovecot version 1 SASL protocol is available in Postfix 2.3 and +later. At the time of writing, only server-side SASL support is available, so +you can't use it to authenticate to your network provider's server. Dovecot +uses its own daemon process for authentication. This keeps the Postfix build +process simple, because there is no need to link extra libraries into Postfix. To generate the necessary Makefiles, execute the following in the Postfix top- level directory: % make makefiles CCARGS='-DUSE_SASL_AUTH - - DDEF_SASL_SERVER_TYPE=\"dovecot\"' + DDEF_SERVER_SASL_TYPE=\"dovecot\"' After this, proceed with "make" as described in the INSTALL document. Notes: - * The "-DDEF_SASL_SERVER_TYPE" stuff is not necessary; it just makes Postfix + * The "-DDEF_SERVER_SASL_TYPE" stuff is not necessary; it just makes Postfix configuration a little more convenient because you don't have to specify the SASL plug-in type in the Postfix main.cf file. diff --git a/postfix/auxiliary/name-addr-test/getaddrinfo.c b/postfix/auxiliary/name-addr-test/getaddrinfo.c new file mode 100644 index 000000000..df551f230 --- /dev/null +++ b/postfix/auxiliary/name-addr-test/getaddrinfo.c @@ -0,0 +1,64 @@ + /* + * getaddrinfo(3) (name->address lookup) tester. + * + * Compile with: + * + * cc -o getaddrinfo getaddrinfo.c (BSD, Linux) + * + * cc -o getaddrinfo getaddrinfo.c -lsocket -lnsl (SunOS 5.x) + * + * Run as: getaddrinfo hostname + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + * + * Author: Wietse Venema, IBM T.J. Watson Research, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char hostbuf[NI_MAXHOST]; /* XXX */ + struct addrinfo hints; + struct addrinfo *res0; + struct addrinfo *res; + const char *addr; + int err; + +#define NO_SERVICE ((char *) 0) + + if (argc != 2) { + fprintf(stderr, "usage: %s hostname\n", argv[0]); + exit(1); + } + memset((char *) &hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + if ((err = getaddrinfo(argv[1], NO_SERVICE, &hints, &res0)) != 0) { + fprintf(stderr, "host %s not found\n", argv[1]); + exit(1); + } + printf("Hostname:\t%s\n", res0->ai_canonname); + printf("Addresses:\t"); + for (res = res0; res != 0; res = res->ai_next) { + addr = (res->ai_family == AF_INET ? + (char *) &((struct sockaddr_in *) res->ai_addr)->sin_addr : + (char *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr); + if (inet_ntop(res->ai_family, addr, hostbuf, sizeof(hostbuf)) == 0) { + perror("inet_ntop:"); + exit(1); + } + printf("%s ", hostbuf); + } + printf("\n"); + freeaddrinfo(res0); + exit(0); +} diff --git a/postfix/auxiliary/name-addr-test/gethostbyaddr.c b/postfix/auxiliary/name-addr-test/gethostbyaddr.c new file mode 100644 index 000000000..e58db9efa --- /dev/null +++ b/postfix/auxiliary/name-addr-test/gethostbyaddr.c @@ -0,0 +1,46 @@ + /* + * gethostbyaddr tester. compile with: + * + * cc -o gethostbyaddr gethostbyaddr.c (SunOS 4.x) + * + * cc -o gethostbyaddr gethostbyaddr.c -lnsl (SunOS 5.x) + * + * run as: gethostbyaddr address + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#include +#include +#include +#include +#include +#include + +main(argc, argv) +int argc; +char **argv; +{ + struct hostent *hp; + long addr; + + if (argc != 2) { + fprintf(stderr, "usage: %s i.p.addres\n", argv[0]); + exit(1); + } + addr = inet_addr(argv[1]); + if (hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) { + printf("Hostname:\t%s\n", hp->h_name); + printf("Aliases:\t"); + while (hp->h_aliases[0]) + printf("%s ", *hp->h_aliases++); + printf("\n"); + printf("Addresses:\t"); + while (hp->h_addr_list[0]) + printf("%s ", inet_ntoa(*(struct in_addr *) * hp->h_addr_list++)); + printf("\n"); + exit(0); + } + fprintf(stderr, "host %s not found\n", argv[1]); + exit(1); +} diff --git a/postfix/auxiliary/name-addr-test/gethostbyname.c b/postfix/auxiliary/name-addr-test/gethostbyname.c new file mode 100644 index 000000000..d8079dd73 --- /dev/null +++ b/postfix/auxiliary/name-addr-test/gethostbyname.c @@ -0,0 +1,44 @@ + /* + * gethostbyname tester. compile with: + * + * cc -o gethostbyname gethostbyname.c (SunOS 4.x) + * + * cc -o gethostbyname gethostbyname.c -lnsl (SunOS 5.x) + * + * run as: gethostbyname hostname + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ +#include +#include +#include +#include +#include +#include + +main(argc, argv) +int argc; +char **argv; +{ + struct hostent *hp; + + if (argc != 2) { + fprintf(stderr, "usage: %s hostname\n", argv[0]); + exit(1); + } + if (hp = gethostbyname(argv[1])) { + printf("Hostname:\t%s\n", hp->h_name); + printf("Aliases:\t"); + while (hp->h_aliases[0]) + printf("%s ", *hp->h_aliases++); + printf("\n"); + printf("Addresses:\t"); + while (hp->h_addr_list[0]) + printf("%s ", inet_ntoa(*(struct in_addr *) * hp->h_addr_list++)); + printf("\n"); + exit(0); + } else { + fprintf(stderr, "host %s not found\n", argv[1]); + exit(1); + } +} diff --git a/postfix/auxiliary/name-addr-test/getnameinfo.c b/postfix/auxiliary/name-addr-test/getnameinfo.c new file mode 100644 index 000000000..a270a062e --- /dev/null +++ b/postfix/auxiliary/name-addr-test/getnameinfo.c @@ -0,0 +1,79 @@ + /* + * getnameinfo(3) (address->name lookup) tester. + * + * Compile with: + * + * cc -o getnameinfo getnameinfo.c (BSD, Linux) + * + * cc -o getnameinfo getnameinfo.c -lsocket -lnsl (SunOS 5.x) + * + * Run as: getnameinfo address + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + * + * Author: Wietse Venema, IBM T.J. Watson Research, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char hostbuf[NI_MAXHOST]; /* XXX */ + struct addrinfo hints; + struct addrinfo *res0; + struct addrinfo *res; + const char *host; + const char *addr; + int err; + +#define NO_SERVICE ((char *) 0) + + if (argc != 2) { + fprintf(stderr, "usage: %s ipaddres\n", argv[0]); + exit(1); + } + + /* + * Convert address to internal form. + */ + host = argv[1]; + memset((char *) &hints, 0, sizeof(hints)); + hints.ai_family = (strchr(host, ':') ? AF_INET6 : AF_INET); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_NUMERICHOST; + if ((err = getaddrinfo(host, NO_SERVICE, &hints, &res0)) != 0) { + fprintf(stderr, "getaddrinfo %s: %s\n", host, gai_strerror(err)); + exit(1); + } + + /* + * Convert host address to name. + */ + for (res = res0; res != 0; res = res->ai_next) { + err = getnameinfo(res->ai_addr, res->ai_addrlen, + hostbuf, sizeof(hostbuf), + NO_SERVICE, 0, NI_NAMEREQD); + if (err) { + fprintf(stderr, "getnameinfo %s: %s\n", host, gai_strerror(err)); + exit(1); + } + printf("Hostname:\t%s\n", hostbuf); + addr = (res->ai_family == AF_INET ? + (char *) &((struct sockaddr_in *) res->ai_addr)->sin_addr : + (char *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr); + if (inet_ntop(res->ai_family, addr, hostbuf, sizeof(hostbuf)) == 0) { + perror("inet_ntop:"); + exit(1); + } + printf("Address:\t%s\n", hostbuf); + } + freeaddrinfo(res0); + exit(0); +} diff --git a/postfix/conf/post-install b/postfix/conf/post-install index 1d919ccfe..c64ff19f6 100644 --- a/postfix/conf/post-install +++ b/postfix/conf/post-install @@ -643,6 +643,7 @@ EOF ed $config_directory/master.cf < -

After postfix has been built with cdb support, you can use +

After Postfix has been built with cdb support, you can use "cdb" tables wherever you can use read-only "hash", "btree" or "dbm" tables. However, the "postmap -i" (incremental record insertion) and "postmap -d" (incremental record deletion) command-line options are not available. For the same reason the "cdb" map type cannot be used to store the persistent address -verification cache for the verify(8) service.

+verification cache for the verify(8) service, or to store +TLS session information for the tlsmgr(8) service.

diff --git a/postfix/html/SASL_README.html b/postfix/html/SASL_README.html index 3086d14e4..20b1a562f 100644 --- a/postfix/html/SASL_README.html +++ b/postfix/html/SASL_README.html @@ -113,9 +113,8 @@ Postfix versions.

Building Postfix with Dovecot SASL support

-

Dovecot SASL support is available in Postfix 2.3 and later. The -Dovecot source code is available via http://www.dovecot.org/. At -the time +

Support for the Dovecot version 1 SASL protocol is available +in Postfix 2.3 and later. At the time of writing, only server-side SASL support is available, so you can't use it to authenticate to your network provider's server. Dovecot uses its own daemon process for authentication. This keeps the @@ -127,7 +126,7 @@ in the Postfix top-level directory:

-% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SASL_SERVER_TYPE=\"dovecot\"'
+% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SERVER_SASL_TYPE=\"dovecot\"'
 
@@ -138,7 +137,7 @@ in the Postfix top-level directory:

    -
  • The "-DDEF_SASL_SERVER_TYPE" stuff is not necessary; it just +

  • The "-DDEF_SERVER_SASL_TYPE" stuff is not necessary; it just makes Postfix configuration a little more convenient because you don't have to specify the SASL plug-in type in the Postfix main.cf file.

    diff --git a/postfix/html/master.5.html b/postfix/html/master.5.html index 06b0f6238..5bddcf7c3 100644 --- a/postfix/html/master.5.html +++ b/postfix/html/master.5.html @@ -147,8 +147,8 @@ MASTER(5) MASTER(5) mented by connecting to the service and sending a wake up request. A ? at the end of the wake-up time field requests that no wake up events be sent - before the service is used. Specify 0 for no auto- - matic wake up. + before the first time a service is used. Specify 0 + for no automatic wake up. The pickup(8), qmgr(8) and flush(8) daemons require a wake up timer. diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index b485dc1d8..3b2d15490 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -5508,6 +5508,11 @@ may wish to turn on the policy (UCE and mail relaying) and protocol error (broken mail software) reports.

    +

    NOTE: postmaster notifications may contain confidential information +such as SASL passwords or message content. It is the system +administrator's responsibility to treat such information with care. +

    +

    The error classes are:

    @@ -11823,7 +11828,8 @@ message contains no To: or Cc: message header.

    The numerical Postfix SMTP server response code when a sender or recipient address is rejected by the reject_unknown_sender_domain -or reject_unknown_recipient_domain restriction. +or reject_unknown_recipient_domain restriction. The response is +always 450 in case of a temporary DNS error.

    diff --git a/postfix/html/qmqp-sink.1.html b/postfix/html/qmqp-sink.1.html index 150462ffb..5811a8066 100644 --- a/postfix/html/qmqp-sink.1.html +++ b/postfix/html/qmqp-sink.1.html @@ -23,6 +23,12 @@ QMQP-SINK(1) QMQP-SINK(1) and IPv6 are the default. This program is the complement of the qmqp-source(1) program. + Note: this is an unsupported test program. No attempt is + made to maintain compatibility between successive ver- + sions. + + Arguments: + -4 Support IPv4 only. This option has no effect when Postfix is built without IPv6 support. diff --git a/postfix/html/qmqp-source.1.html b/postfix/html/qmqp-source.1.html index 27e8fa47b..652741b7b 100644 --- a/postfix/html/qmqp-source.1.html +++ b/postfix/html/qmqp-source.1.html @@ -21,7 +21,11 @@ QMQP-SOURCE(1) QMQP-SOURCE(1) protocol. Connections can be made to UNIX-domain and IPv4 or IPv6 servers. IPv4 and IPv6 are the default. - Options: + Note: this is an unsupported test program. No attempt is + made to maintain compatibility between successive ver- + sions. + + Arguments: -4 Connect to the server with IPv4. This option has no effect when Postfix is built without IPv6 support. diff --git a/postfix/html/smtp-sink.1.html b/postfix/html/smtp-sink.1.html index dcd9623de..1880535a9 100644 --- a/postfix/html/smtp-sink.1.html +++ b/postfix/html/smtp-sink.1.html @@ -20,11 +20,21 @@ SMTP-SINK(1) SMTP-SINK(1) away. The purpose is to measure client performance, not protocol compliance. + smtp-sink may also be configured to capture each mail + delivery transaction to file. Since disk latencies are + large compared to network delays, this mode of operation + can reduce the maximal performance by several orders of + magnitude. + Connections can be accepted on IPv4 or IPv6 endpoints, or on UNIX-domain sockets. IPv4 and IPv6 are the default. This program is the complement of the smtp-source(1) pro- gram. + Note: this is an unsupported test program. No attempt is + made to maintain compatibility between successive ver- + sions. + Arguments: -4 Support IPv4 only. This option has no effect when @@ -43,6 +53,30 @@ SMTP-SINK(1) SMTP-SINK(1) -C Disable XCLIENT support. + -d dump-template + Dump each mail transaction to a single-message file + whose name is created by expanding the dump-tem- + plate via strftime(3) and appending a pseudo-random + hexadecimal number (example: "%Y%m%d%H/%M." expands + into "2006081203/05.809a62e3"). If the template + contains "/" characters, missing directories are + created automatically. The message dump format is + described below. + + Note: this option keeps one capture file open for + every mail transaction in progress. + + -D dump-template + Append mail transactions to a multi-message dump + file whose name is created by expanding the dump- + template via strftime(3). If the template contains + "/" characters, missing directories are created + automatically. The message dump format is + described below. + + Note: this option keeps one capture file open for + every mail transaction in progress. + -e Do not announce ESMTP support. -E Do not announce ENHANCEDSTATUSCODES support. @@ -66,6 +100,13 @@ SMTP-SINK(1) SMTP-SINK(1) -L Enable LMTP instead of SMTP. + -m count (default: 256) + An upper bound on the maximal number of simultane- + ous connections that smtp-sink will handle. This + prevents the process from running out of file + descriptors. Excess connections will stay queued in + the TCP/IP stack. + -n count Terminate after count sessions. This is for testing purposes. @@ -105,9 +146,19 @@ SMTP-SINK(1) SMTP-SINK(1) and use quotes to protect white space from the shell. Command names are case-insensitive. + -S start-string + An optional string that is prepended to each mes- + sage that is written to a dump file (see the dump + file format description below). The following C + escape sequences are supported: \a (bell), \b + (backslace), \f (formfeed), \n (newline), \r (car- + riage return), \t (horizontal tab), \v (vertical + tab), \ddd (up to three octal digits) and \\ (the + backslash character). + -t timeout (default: 100) Limit the time for receiving a command or sending a - response. The time limit is specified in seconds. + response. The time limit is specified in seconds. -v Show the SMTP conversations. @@ -116,7 +167,7 @@ SMTP-SINK(1) SMTP-SINK(1) mand. [inet:][host]:port - Listen on network interface host (default: any + Listen on network interface host (default: any interface) TCP port port. Both host and port may be specified in numeric or symbolic form. @@ -124,9 +175,76 @@ SMTP-SINK(1) SMTP-SINK(1) Listen on the UNIX-domain socket at pathname. backlog - The maximum length the queue of pending connec- + The maximum length the queue of pending connec- tions, as defined by the listen(2) system call. +DUMP FILE FORMAT + Each dumped message contains a sequence of text lines, + terminated with the newline character. The sequence of + information is as follows: + + o The optional string specified with the -S option. + + o The smtp-sink generated headers as documented + below. + + o The message header and body as received from the + SMTP client. + + o An empty line. + + The format of the smtp-sink generated headers is as fol- + lows: + + X-Client-Addr: text + The client IP address without enclosing []. An IPv6 + address is prefixed with "ipv6:". This record is + always present. + + X-Client-Proto: text + The client protocol: SMTP, ESMTP or LMTP. This + record is always present. + + X-Helo-Args: text + The arguments of the last HELO or EHLO command + before this mail delivery transaction. This record + is present only if the client sent a recognizable + HELO or EHLO command before the DATA command. + + X-Mail-Args: text + The arguments, if any, of the MAIL command that + started this mail delivery transaction. This record + is present only if the client sent a recognizable + MAIL command. + + X-Rcpt-Args: text + The arguments, if any, of each successive RCPT com- + mand within this mail delivery transaction. There + may be zero or more of these records. This record + is present only if the client sent a recognizable + RCPT command. + + Received: text + A message header for compatibility with mail pro- + cessing software. This three-line header marks the + end of the headers provided by smtp-sink, and is + formatted as follows: + + from helo ([addr]) + The HELO or EHLO command argument and client + IP address. If the client did not send HELO + or EHLO, the client IP address is used + instead. + + by host (smtp-sink) with proto id random; + The hostname specified with the -h option, + the client protocol (see X-Client-Proto + above), and the pseudo-random portion of the + per-message capture file name. + + time-stamp + A time stamp as defined in RFC 2822. + SEE ALSO smtp-source(1), SMTP/LMTP message generator diff --git a/postfix/html/smtp-source.1.html b/postfix/html/smtp-source.1.html index 19e882d66..31f285055 100644 --- a/postfix/html/smtp-source.1.html +++ b/postfix/html/smtp-source.1.html @@ -22,6 +22,10 @@ SMTP-SOURCE(1) SMTP-SOURCE(1) UNIX-domain and IPv4 or IPv6 servers. IPv4 and IPv6 are the default. + Note: this is an unsupported test program. No attempt is + made to maintain compatibility between successive ver- + sions. + Arguments: -4 Connect to the server with IPv4. This option has no diff --git a/postfix/man/man1/qmqp-sink.1 b/postfix/man/man1/qmqp-sink.1 index 07f6ad0e5..f26c1c9c2 100644 --- a/postfix/man/man1/qmqp-sink.1 +++ b/postfix/man/man1/qmqp-sink.1 @@ -25,6 +25,11 @@ Connections can be accepted on IPv4 or IPv6 endpoints, or on UNIX-domain sockets. IPv4 and IPv6 are the default. This program is the complement of the \fBqmqp-source\fR(1) program. + +Note: this is an unsupported test program. No attempt is made +to maintain compatibility between successive versions. + +Arguments: .IP \fB-4\fR Support IPv4 only. This option has no effect when Postfix is built without IPv6 support. diff --git a/postfix/man/man1/qmqp-source.1 b/postfix/man/man1/qmqp-source.1 index 668c595a8..9cdfb476c 100644 --- a/postfix/man/man1/qmqp-source.1 +++ b/postfix/man/man1/qmqp-source.1 @@ -21,7 +21,10 @@ or in parallel. The program speaks the QMQP protocol. Connections can be made to UNIX-domain and IPv4 or IPv6 servers. IPv4 and IPv6 are the default. -Options: +Note: this is an unsupported test program. No attempt is made +to maintain compatibility between successive versions. + +Arguments: .IP \fB-4\fR Connect to the server with IPv4. This option has no effect when Postfix is built without IPv6 support. diff --git a/postfix/man/man1/smtp-sink.1 b/postfix/man/man1/smtp-sink.1 index 6a7925850..dff7310b2 100644 --- a/postfix/man/man1/smtp-sink.1 +++ b/postfix/man/man1/smtp-sink.1 @@ -21,11 +21,19 @@ It takes SMTP messages from the network and throws them away. The purpose is to measure client performance, not protocol compliance. +\fBsmtp-sink\fR may also be configured to capture each mail +delivery transaction to file. Since disk latencies are large +compared to network delays, this mode of operation can +reduce the maximal performance by several orders of magnitude. + Connections can be accepted on IPv4 or IPv6 endpoints, or on UNIX-domain sockets. IPv4 and IPv6 are the default. This program is the complement of the \fBsmtp-source\fR(1) program. +Note: this is an unsupported test program. No attempt is made +to maintain compatibility between successive versions. + Arguments: .IP \fB-4\fR Support IPv4 only. This option has no effect when @@ -43,6 +51,27 @@ session ends, a QUIT command is executed, or when "." is received. .IP \fB-C\fR Disable XCLIENT support. +.IP "\fB-d \fIdump-template\fR" +Dump each mail transaction to a single-message file whose +name is created by expanding the \fIdump-template\fR via +strftime(3) and appending a pseudo-random hexadecimal number +(example: "%Y%m%d%H/%M." expands into "2006081203/05.809a62e3"). +If the template contains "/" characters, missing directories +are created automatically. The message dump format is +described below. +.sp +Note: this option keeps one capture file open for every +mail transaction in progress. +.IP "\fB-D \fIdump-template\fR" +Append mail transactions to a multi-message dump file whose +name is created by expanding the \fIdump-template\fR via +strftime(3). +If the template contains "/" characters, missing directories +are created automatically. The message dump format is +described below. +.sp +Note: this option keeps one capture file open for every +mail transaction in progress. .IP \fB-e\fR Do not announce ESMTP support. .IP \fB-E\fR @@ -62,6 +91,11 @@ Use \fIhostname\fR in the SMTP greeting, in the HELO response, and in the EHLO response. The default hostname is "smtp-sink". .IP \fB-L\fR Enable LMTP instead of SMTP. +.IP "\fB-m \fIcount\fR (default: 256)" +An upper bound on the maximal number of simultaneous +connections that \fBsmtp-sink\fR will handle. This prevents +the process from running out of file descriptors. Excess +connections will stay queued in the TCP/IP stack. .IP "\fB-n \fIcount\fR" Terminate after \fIcount\fR sessions. This is for testing purposes. .IP \fB-p\fR @@ -92,6 +126,14 @@ Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT, VRFY, DATA, ., RSET, NOOP, and QUIT. Separate command names by white space or commas, and use quotes to protect white space from the shell. Command names are case-insensitive. +.IP "\fB-S start-string\fR" +An optional string that is prepended to each message that is +written to a dump file (see the dump file format description +below). The following C escape sequences are supported: \ea +(bell), \eb (backslace), \ef (formfeed), \en (newline), \er +(carriage return), \et (horizontal tab), \ev (vertical tab), +\e\fIddd\fR (up to three octal digits) and \e\e (the backslash +character). .IP "\fB-t \fItimeout\fR (default: 100)" Limit the time for receiving a command or sending a response. The time limit is specified in seconds. @@ -108,6 +150,62 @@ Listen on the UNIX-domain socket at \fIpathname\fR. .IP \fIbacklog\fR The maximum length the queue of pending connections, as defined by the \fBlisten\fR(2) system call. +.SH "DUMP FILE FORMAT" +.na +.nf +.ad +.fi +Each dumped message contains a sequence of text lines, +terminated with the newline character. The sequence of +information is as follows: +.IP \(bu +The optional string specified with the \fB-S\fR option. +.IP \(bu +The \fBsmtp-sink\fR generated headers as documented below. +.IP \(bu +The message header and body as received from the SMTP client. +.IP \(bu +An empty line. +.PP +The format of the \fBsmtp-sink\fR generated headers is as +follows: +.IP "\fBX-Client-Addr: \fItext\fR" +The client IP address without enclosing []. An IPv6 address +is prefixed with "ipv6:". This record is always present. +.IP "\fBX-Client-Proto: \fItext\fR" +The client protocol: SMTP, ESMTP or LMTP. This record is +always present. +.IP "\fBX-Helo-Args: \fItext\fR" +The arguments of the last HELO or EHLO command before this +mail delivery transaction. This record is present only if +the client sent a recognizable HELO or EHLO command before +the DATA command. +.IP "\fBX-Mail-Args: \fItext\fR" +The arguments, if any, of the MAIL command that started +this mail delivery transaction. This record is present only +if the client sent a recognizable MAIL command. +.IP "\fBX-Rcpt-Args: \fItext\fR" +The arguments, if any, of each successive RCPT command +within this mail delivery transaction. There may be zero +or more of these records. This record is present only if +the client sent a recognizable RCPT command. +.IP "\fBReceived: \fItext\fR" +A message header for compatibility with mail processing +software. This three-line header marks the end of the headers +provided by \fBsmtp-sink\fR, and is formatted +as follows: +.RS +.IP "\fBfrom \fIhelo\fB ([\fIaddr\fB])\fR" +The HELO or EHLO command argument and client IP address. +If the client did not send HELO or EHLO, the client IP +address is used instead. +.IP "\fBby \fIhost\fB (smtp-sink) with \fIproto\fB id \fIrandom\fB;\fR" +The hostname specified with the \fB-h\fR option, the client +protocol (see \fBX-Client-Proto\fR above), and the pseudo-random +portion of the per-message capture file name. +.IP \fItime-stamp\fR +A time stamp as defined in RFC 2822. +.RE .SH "SEE ALSO" .na .nf diff --git a/postfix/man/man1/smtp-source.1 b/postfix/man/man1/smtp-source.1 index 594363cf7..0dabccf98 100644 --- a/postfix/man/man1/smtp-source.1 +++ b/postfix/man/man1/smtp-source.1 @@ -23,6 +23,9 @@ LMTP. Connections can be made to UNIX-domain and IPv4 or IPv6 servers. IPv4 and IPv6 are the default. +Note: this is an unsupported test program. No attempt is made +to maintain compatibility between successive versions. + Arguments: .IP \fB-4\fR Connect to the server with IPv4. This option has no effect when diff --git a/postfix/man/man5/master.5 b/postfix/man/man5/master.5 index 012b1331a..632a5af0f 100644 --- a/postfix/man/man5/master.5 +++ b/postfix/man/man5/master.5 @@ -134,7 +134,7 @@ Automatically wake up the named service after the specified number of seconds. The wake up is implemented by connecting to the service and sending a wake up request. A ? at the end of the wake-up time field requests that no wake up -events be sent before the service is used. +events be sent before the first time a service is used. Specify 0 for no automatic wake up. .sp The \fBpickup\fR(8), \fBqmgr\fR(8) and \fBflush\fR(8) diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 8484d0216..c37d800ba 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -3019,6 +3019,10 @@ default is to report only the most serious problems. The paranoid may wish to turn on the policy (UCE and mail relaying) and protocol error (broken mail software) reports. .PP +NOTE: postmaster notifications may contain confidential information +such as SASL passwords or message content. It is the system +administrator's responsibility to treat such information with care. +.PP The error classes are: .IP "\fBbounce\fR (also implies \fB2bounce\fR)" Send the postmaster copies of the headers of bounced mail, and @@ -7164,7 +7168,8 @@ message contains no To: or Cc: message header. .SH unknown_address_reject_code (default: 450) The numerical Postfix SMTP server response code when a sender or recipient address is rejected by the reject_unknown_sender_domain -or reject_unknown_recipient_domain restriction. +or reject_unknown_recipient_domain restriction. The response is +always 450 in case of a temporary DNS error. .PP Do not change this unless you have a complete understanding of RFC 821. .SH unknown_client_reject_code (default: 450) diff --git a/postfix/proto/CDB_README.html b/postfix/proto/CDB_README.html index fd56bdc2e..a025cd3e5 100644 --- a/postfix/proto/CDB_README.html +++ b/postfix/proto/CDB_README.html @@ -79,10 +79,11 @@ like:

    -

    After postfix has been built with cdb support, you can use +

    After Postfix has been built with cdb support, you can use "cdb" tables wherever you can use read-only "hash", "btree" or "dbm" tables. However, the "postmap -i" (incremental record insertion) and "postmap -d" (incremental record deletion) command-line options are not available. For the same reason the "cdb" map type cannot be used to store the persistent address -verification cache for the verify(8) service.

    +verification cache for the verify(8) service, or to store +TLS session information for the tlsmgr(8) service.

    diff --git a/postfix/proto/SASL_README.html b/postfix/proto/SASL_README.html index cd6d23ab1..b9c662aaf 100644 --- a/postfix/proto/SASL_README.html +++ b/postfix/proto/SASL_README.html @@ -113,9 +113,8 @@ Postfix versions.

    Building Postfix with Dovecot SASL support

    -

    Dovecot SASL support is available in Postfix 2.3 and later. The -Dovecot source code is available via http://www.dovecot.org/. At -the time +

    Support for the Dovecot version 1 SASL protocol is available +in Postfix 2.3 and later. At the time of writing, only server-side SASL support is available, so you can't use it to authenticate to your network provider's server. Dovecot uses its own daemon process for authentication. This keeps the @@ -127,7 +126,7 @@ in the Postfix top-level directory:

    -% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SASL_SERVER_TYPE=\"dovecot\"'
    +% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SERVER_SASL_TYPE=\"dovecot\"'
     
    @@ -138,7 +137,7 @@ INSTALL document.

      -
    • The "-DDEF_SASL_SERVER_TYPE" stuff is not necessary; it just +

    • The "-DDEF_SERVER_SASL_TYPE" stuff is not necessary; it just makes Postfix configuration a little more convenient because you don't have to specify the SASL plug-in type in the Postfix main.cf file.

      diff --git a/postfix/proto/master b/postfix/proto/master index 4720caa8a..57674b13d 100644 --- a/postfix/proto/master +++ b/postfix/proto/master @@ -128,7 +128,7 @@ # number of seconds. The wake up is implemented by connecting # to the service and sending a wake up request. A ? at the # end of the wake-up time field requests that no wake up -# events be sent before the service is used. +# events be sent before the first time a service is used. # Specify 0 for no automatic wake up. # .sp # The \fBpickup\fR(8), \fBqmgr\fR(8) and \fBflush\fR(8) diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 0918f8db9..f031b6f57 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -2801,6 +2801,11 @@ may wish to turn on the policy (UCE and mail relaying) and protocol error (broken mail software) reports.

      +

      NOTE: postmaster notifications may contain confidential information +such as SASL passwords or message content. It is the system +administrator's responsibility to treat such information with care. +

      +

      The error classes are:

      @@ -5894,7 +5899,8 @@ The default time unit is s (seconds).

      The numerical Postfix SMTP server response code when a sender or recipient address is rejected by the reject_unknown_sender_domain -or reject_unknown_recipient_domain restriction. +or reject_unknown_recipient_domain restriction. The response is +always 450 in case of a temporary DNS error.

      diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c index e4acad297..c01d56ef1 100644 --- a/postfix/src/cleanup/cleanup_milter.c +++ b/postfix/src/cleanup/cleanup_milter.c @@ -213,10 +213,13 @@ static void cleanup_milter_set_error(CLEANUP_STATE *state, int err) { - if (err == EFBIG) + if (err == EFBIG) { + msg_warn("%s: queue file size limit exceeded", state->queue_id); state->errs |= CLEANUP_STAT_SIZE; - else + } else { + msg_warn("%s: write queue file: %m", state->queue_id); state->errs |= CLEANUP_STAT_WRITE; + } } /* cleanup_milter_error - return dummy error description */ diff --git a/postfix/src/global/mail_date.c b/postfix/src/global/mail_date.c index 93edfe9c2..55d890776 100644 --- a/postfix/src/global/mail_date.c +++ b/postfix/src/global/mail_date.c @@ -121,7 +121,7 @@ const char *mail_date(time_t when) * Finally, add the time zone name. */ while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0) - VSTRING_SPACE(vp, 100); + VSTRING_SPACE(vp, vstring_avail(vp) + 100); VSTRING_SKIP(vp); return (vstring_str(vp)); diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index ed34b8c06..493917922 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20060806" +#define MAIL_RELEASE_DATE "20060823" #define MAIL_VERSION_NUMBER "2.4" #ifdef SNAPSHOT diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 8dcc789b4..7447a2cff 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -2250,7 +2250,7 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) } vstring_strcpy(state->dsn_orcpt_buf, arg + 6); if (dsn_orcpt_addr - || (coded_addr = split_at(STR(state->dsn_orcpt_buf), ';')) == 0 + || (coded_addr = split_at(STR(state->dsn_orcpt_buf), ';')) == 0 || xtext_unquote(state->dsn_buf, coded_addr) == 0 || *(dsn_orcpt_type = STR(state->dsn_orcpt_buf)) == 0) { state->error_mask |= MAIL_ERROR_PROTOCOL; @@ -2643,9 +2643,11 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) && (state->proxy == 0 ? (++start, --len) == 0 : len == 1)) break; if (state->err == CLEANUP_STAT_OK) { - if (var_message_limit > 0 && var_message_limit - state->act_size < len + 2) + if (var_message_limit > 0 && var_message_limit - state->act_size < len + 2) { state->err = CLEANUP_STAT_SIZE; - else { + msg_warn("%s: queue file size limit exceeded", + state->queue_id ? state->queue_id : "NOQUEUE"); + } else { state->act_size += len + 2; if (out_record(out_stream, curr_rec_type, start, len) < 0) state->err = out_error; diff --git a/postfix/src/smtpstone/qmqp-sink.c b/postfix/src/smtpstone/qmqp-sink.c index 2116b1bda..30390e627 100644 --- a/postfix/src/smtpstone/qmqp-sink.c +++ b/postfix/src/smtpstone/qmqp-sink.c @@ -19,6 +19,11 @@ /* UNIX-domain sockets. /* IPv4 and IPv6 are the default. /* This program is the complement of the \fBqmqp-source\fR(1) program. +/* +/* Note: this is an unsupported test program. No attempt is made +/* to maintain compatibility between successive versions. +/* +/* Arguments: /* .IP \fB-4\fR /* Support IPv4 only. This option has no effect when /* Postfix is built without IPv6 support. diff --git a/postfix/src/smtpstone/qmqp-source.c b/postfix/src/smtpstone/qmqp-source.c index d06054215..2606e6e11 100644 --- a/postfix/src/smtpstone/qmqp-source.c +++ b/postfix/src/smtpstone/qmqp-source.c @@ -15,7 +15,10 @@ /* Connections can be made to UNIX-domain and IPv4 or IPv6 servers. /* IPv4 and IPv6 are the default. /* -/* Options: +/* Note: this is an unsupported test program. No attempt is made +/* to maintain compatibility between successive versions. +/* +/* Arguments: /* .IP \fB-4\fR /* Connect to the server with IPv4. This option has no effect when /* Postfix is built without IPv6 support. diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c index 289c2f1a3..dc151c882 100644 --- a/postfix/src/smtpstone/smtp-sink.c +++ b/postfix/src/smtpstone/smtp-sink.c @@ -15,11 +15,19 @@ /* The purpose is to measure client performance, not protocol /* compliance. /* +/* \fBsmtp-sink\fR may also be configured to capture each mail +/* delivery transaction to file. Since disk latencies are large +/* compared to network delays, this mode of operation can +/* reduce the maximal performance by several orders of magnitude. +/* /* Connections can be accepted on IPv4 or IPv6 endpoints, or on /* UNIX-domain sockets. /* IPv4 and IPv6 are the default. /* This program is the complement of the \fBsmtp-source\fR(1) program. /* +/* Note: this is an unsupported test program. No attempt is made +/* to maintain compatibility between successive versions. +/* /* Arguments: /* .IP \fB-4\fR /* Support IPv4 only. This option has no effect when @@ -37,6 +45,27 @@ /* received. /* .IP \fB-C\fR /* Disable XCLIENT support. +/* .IP "\fB-d \fIdump-template\fR" +/* Dump each mail transaction to a single-message file whose +/* name is created by expanding the \fIdump-template\fR via +/* strftime(3) and appending a pseudo-random hexadecimal number +/* (example: "%Y%m%d%H/%M." expands into "2006081203/05.809a62e3"). +/* If the template contains "/" characters, missing directories +/* are created automatically. The message dump format is +/* described below. +/* .sp +/* Note: this option keeps one capture file open for every +/* mail transaction in progress. +/* .IP "\fB-D \fIdump-template\fR" +/* Append mail transactions to a multi-message dump file whose +/* name is created by expanding the \fIdump-template\fR via +/* strftime(3). +/* If the template contains "/" characters, missing directories +/* are created automatically. The message dump format is +/* described below. +/* .sp +/* Note: this option keeps one capture file open for every +/* mail transaction in progress. /* .IP \fB-e\fR /* Do not announce ESMTP support. /* .IP \fB-E\fR @@ -56,6 +85,11 @@ /* and in the EHLO response. The default hostname is "smtp-sink". /* .IP \fB-L\fR /* Enable LMTP instead of SMTP. +/* .IP "\fB-m \fIcount\fR (default: 256)" +/* An upper bound on the maximal number of simultaneous +/* connections that \fBsmtp-sink\fR will handle. This prevents +/* the process from running out of file descriptors. Excess +/* connections will stay queued in the TCP/IP stack. /* .IP "\fB-n \fIcount\fR" /* Terminate after \fIcount\fR sessions. This is for testing purposes. /* .IP \fB-p\fR @@ -86,6 +120,14 @@ /* DATA, ., RSET, NOOP, and QUIT. Separate command names by /* white space or commas, and use quotes to protect white space /* from the shell. Command names are case-insensitive. +/* .IP "\fB-S start-string\fR" +/* An optional string that is prepended to each message that is +/* written to a dump file (see the dump file format description +/* below). The following C escape sequences are supported: \ea +/* (bell), \eb (backslace), \ef (formfeed), \en (newline), \er +/* (carriage return), \et (horizontal tab), \ev (vertical tab), +/* \e\fIddd\fR (up to three octal digits) and \e\e (the backslash +/* character). /* .IP "\fB-t \fItimeout\fR (default: 100)" /* Limit the time for receiving a command or sending a response. /* The time limit is specified in seconds. @@ -102,6 +144,60 @@ /* .IP \fIbacklog\fR /* The maximum length the queue of pending connections, /* as defined by the \fBlisten\fR(2) system call. +/* DUMP FILE FORMAT +/* .ad +/* .fi +/* Each dumped message contains a sequence of text lines, +/* terminated with the newline character. The sequence of +/* information is as follows: +/* .IP \(bu +/* The optional string specified with the \fB-S\fR option. +/* .IP \(bu +/* The \fBsmtp-sink\fR generated headers as documented below. +/* .IP \(bu +/* The message header and body as received from the SMTP client. +/* .IP \(bu +/* An empty line. +/* .PP +/* The format of the \fBsmtp-sink\fR generated headers is as +/* follows: +/* .IP "\fBX-Client-Addr: \fItext\fR" +/* The client IP address without enclosing []. An IPv6 address +/* is prefixed with "ipv6:". This record is always present. +/* .IP "\fBX-Client-Proto: \fItext\fR" +/* The client protocol: SMTP, ESMTP or LMTP. This record is +/* always present. +/* .IP "\fBX-Helo-Args: \fItext\fR" +/* The arguments of the last HELO or EHLO command before this +/* mail delivery transaction. This record is present only if +/* the client sent a recognizable HELO or EHLO command before +/* the DATA command. +/* .IP "\fBX-Mail-Args: \fItext\fR" +/* The arguments, if any, of the MAIL command that started +/* this mail delivery transaction. This record is present only +/* if the client sent a recognizable MAIL command. +/* .IP "\fBX-Rcpt-Args: \fItext\fR" +/* The arguments, if any, of each successive RCPT command +/* within this mail delivery transaction. There may be zero +/* or more of these records. This record is present only if +/* the client sent a recognizable RCPT command. +/* .IP "\fBReceived: \fItext\fR" +/* A message header for compatibility with mail processing +/* software. This three-line header marks the end of the headers +/* provided by \fBsmtp-sink\fR, and is formatted +/* as follows: +/* .RS +/* .IP "\fBfrom \fIhelo\fB ([\fIaddr\fB])\fR" +/* The HELO or EHLO command argument and client IP address. +/* If the client did not send HELO or EHLO, the client IP +/* address is used instead. +/* .IP "\fBby \fIhost\fB (smtp-sink) with \fIproto\fB id \fIrandom\fB;\fR" +/* The hostname specified with the \fB-h\fR option, the client +/* protocol (see \fBX-Client-Proto\fR above), and the pseudo-random +/* portion of the per-message capture file name. +/* .IP \fItime-stamp\fR +/* A time stamp as defined in RFC 2822. +/* .RE /* SEE ALSO /* smtp-source(1), SMTP/LMTP message generator /* LICENSE @@ -120,12 +216,15 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #ifdef STRCASECMP_IN_STRINGS_H #include @@ -146,10 +245,14 @@ #include #include #include +#include +#include +#include /* Global library. */ #include +#include /* Application-specific. */ @@ -158,8 +261,17 @@ typedef struct SINK_STATE { VSTRING *buffer; int data_state; int (*read_fn) (struct SINK_STATE *); + int in_mail; int rcpts; char *push_back_ptr; + /* Capture file information for fake Received: header */ + MAI_HOSTADDR_STR client_addr; /* IP address */ + char *addr_prefix; /* ipv6: or empty */ + char *helo_args; /* text after HELO or EHLO */ + const char *client_proto; /* SMTP, ESMTP, LMTP */ + time_t start_time; /* MAIL command time */ + int id; /* pseudo-random */ + VSTREAM *dump_file; /* dump file or null */ } SINK_STATE; #define ST_ANY 0 @@ -173,6 +285,10 @@ typedef struct SINK_STATE { #define PUSH_BACK_GET(state) (*(state)->push_back_ptr++) #define PUSH_BACK_SET(state, text) ((state)->push_back_ptr = (text)) +#ifndef DEF_MAX_CLIENT_COUNT +#define DEF_MAX_CLIENT_COUNT 256 +#endif + static int var_tmout = 100; static int var_max_line_length = 2048; static char *var_myhostname; @@ -194,10 +310,19 @@ static int disable_saslauth; static int disable_xclient; static int disable_xforward; static int disable_enh_status; +static int max_client_count = DEF_MAX_CLIENT_COUNT; +static int client_count; +static int sock; + +static char *single_template; /* individual template */ +static char *shared_template; /* shared template */ +static VSTRING *start_string; /* dump content prefix */ #define SOFT_ERROR_RESP "450 4.3.0 Error: command failed" #define HARD_ERROR_RESP "500 5.3.0 Error: command failed" +#define STR(x) vstring_str(x) + /* do_stats - show counters */ static void do_stats(void) @@ -223,10 +348,211 @@ static void soft_err_resp(SINK_STATE *state) smtp_flush(state->stream); } +/* exp_path_template - expand template pathname, static result */ + +static VSTRING *exp_path_template(const char *template, time_t start_time) +{ + static VSTRING *path_buf = 0; + struct tm *lt; + + if (path_buf == 0) + path_buf = vstring_alloc(100); + else + VSTRING_RESET(path_buf); + lt = localtime(&start_time); + while (strftime(STR(path_buf), vstring_avail(path_buf), template, lt) == 0) + VSTRING_SPACE(path_buf, vstring_avail(path_buf) + 100); + VSTRING_SKIP(path_buf); + return (path_buf); +} + +/* make_parent_dir - create parent directory or bust */ + +static void make_parent_dir(const char *path, mode_t mode) +{ + const char *parent; + + parent = sane_dirname((VSTRING *) 0, path); + if (make_dirs(parent, mode) < 0) + msg_fatal("mkdir %s: %m", parent); +} + +/* mail_file_open - open mail capture file */ + +static void mail_file_open(SINK_STATE *state) +{ + const char *myname = "mail_file_open"; + VSTRING *path_buf; + ssize_t len; + int tries = 0; + + /* + * Save the start time for later. + */ + time(&(state->start_time)); + + /* + * Expand the per-message dumpfile pathname template. + */ + path_buf = exp_path_template(single_template, state->start_time); + + /* + * Append a random hexadecimal string to the pathname and create a new + * file. Retry with a different path if the file already exists. Create + * intermediate directories on the fly when the template specifies + * multiple pathname segments. + */ +#define ID_FORMAT "%08x" + + for (len = VSTRING_LEN(path_buf); /* void */ ; vstring_truncate(path_buf, len)) { + if (++tries > 100) + msg_fatal("%s: something is looping", myname); + state->id = myrand(); + vstring_sprintf_append(path_buf, ID_FORMAT, state->id); + if ((state->dump_file = vstream_fopen(STR(path_buf), + O_RDWR | O_CREAT | O_EXCL, + 0644)) != 0) { + break; + } else if (errno == EEXIST) { + continue; + } else if (errno == ENOENT) { + make_parent_dir(STR(path_buf), 0755); + continue; + } else { + msg_fatal("open %s: %m", STR(path_buf)); + } + } + + /* + * Don't leave temporary files behind. + */ + if (shared_template != 0 && unlink(STR(path_buf)) < 0) + msg_fatal("unlink %s: %m", STR(path_buf)); + + /* + * Do initial header records. + */ + if (start_string) + vstream_fprintf(state->dump_file, "%s", STR(start_string)); + vstream_fprintf(state->dump_file, "X-Client-Addr: %s%s\n", + state->addr_prefix, state->client_addr.buf); + vstream_fprintf(state->dump_file, "X-Client-Proto: %s\n", state->client_proto); + if (state->helo_args) + vstream_fprintf(state->dump_file, "X-Helo-Args: %s\n", state->helo_args); + /* Note: there may be more than one recipient. */ +} + +/* mail_file_finish_header - do final smtp-sink generated header records */ + +static void mail_file_finish_header(SINK_STATE *state) +{ + if (state->helo_args) + vstream_fprintf(state->dump_file, "Received: from %s ([%s%s])\n", + state->helo_args, state->addr_prefix, + state->client_addr.buf); + else + vstream_fprintf(state->dump_file, "Received: from [%s%s] ([%s%s])\n", + state->addr_prefix, state->client_addr.buf, + state->addr_prefix, state->client_addr.buf); + vstream_fprintf(state->dump_file, "\tby %s (smtp-sink)" + " with %s id " ID_FORMAT ";\n", + var_myhostname, state->client_proto, state->id); + vstream_fprintf(state->dump_file, "\t%s\n", mail_date(state->start_time)); +} + +/* mail_file_cleanup - common cleanup for capture file */ + +static void mail_file_cleanup(SINK_STATE *state) +{ + (void) vstream_fclose(state->dump_file); + state->dump_file = 0; +} + +/* mail_file_finish - handle message completion for capture file */ + +static void mail_file_finish(SINK_STATE *state) +{ + + /* + * Optionally append the captured message to a shared dumpfile. + */ + if (shared_template) { + const char *out_path; + VSTREAM *out_fp; + ssize_t count; + + /* + * Expand the shared dumpfile pathname template. + */ + out_path = STR(exp_path_template(shared_template, state->start_time)); + + /* + * Open the shared dump file. + */ +#define OUT_OPEN_FLAGS (O_WRONLY | O_CREAT | O_APPEND) +#define OUT_OPEN_MODE 0644 + + if ((out_fp = vstream_fopen(out_path, OUT_OPEN_FLAGS, OUT_OPEN_MODE)) + == 0 && errno == ENOENT) { + make_parent_dir(out_path, 0755); + out_fp = vstream_fopen(out_path, OUT_OPEN_FLAGS, OUT_OPEN_MODE); + } + if (out_fp == 0) + msg_fatal("open %s: %m", out_path); + + /* + * Append message content from single-message dump file. + */ + if (vstream_fseek(state->dump_file, 0L, SEEK_SET) < 0) + msg_fatal("seek file %s: %m", VSTREAM_PATH(state->dump_file)); + VSTRING_RESET(state->buffer); + for (;;) { + count = vstream_fread(state->dump_file, STR(state->buffer), + vstring_avail(state->buffer)); + if (count <= 0) + break; + if (vstream_fwrite(out_fp, STR(state->buffer), count) != count) + msg_fatal("append file %s: %m", out_path); + } + if (vstream_ferror(state->dump_file)) + msg_fatal("read file %s: %m", VSTREAM_PATH(state->dump_file)); + if (vstream_fclose(out_fp)) + msg_fatal("append file %s: %m", out_path); + } + mail_file_cleanup(state); +} + +/* mail_file_reset - abort mail to capture file */ + +static void mail_file_reset(SINK_STATE *state) +{ + if (shared_template == 0 + && unlink(VSTREAM_PATH(state->dump_file)) < 0 + && errno != ENOENT) + msg_fatal("unlink %s: %m", VSTREAM_PATH(state->dump_file)); + mail_file_cleanup(state); +} + +/* mail_cmd_reset - reset mail transaction information */ + +static void mail_cmd_reset(SINK_STATE *state) +{ + state->in_mail = 0; + /* Not: state->rcpts = 0. This breaks the DOT reply with LMTP. */ + if (state->dump_file) + mail_file_reset(state); +} + /* ehlo_response - respond to EHLO command */ -static void ehlo_response(SINK_STATE *state) +static void ehlo_response(SINK_STATE *state, const char *args) { +#define SKIP(cp, cond) for (/* void */; *cp && (cond); cp++) + + /* EHLO aborts a mail transaction in progress. */ + mail_cmd_reset(state); + if (enable_lmtp == 0) + state->client_proto = "ESMTP"; smtp_printf(state->stream, "250-%s", var_myhostname); if (!disable_pipelining) smtp_printf(state->stream, "250-PIPELINING"); @@ -242,50 +568,107 @@ static void ehlo_response(SINK_STATE *state) smtp_printf(state->stream, "250-ENHANCEDSTATUSCODES"); smtp_printf(state->stream, "250 "); smtp_flush(state->stream); + if (single_template) { + if (state->helo_args) + myfree(state->helo_args); + SKIP(args, ISSPACE(*args)); + state->helo_args = mystrdup(args); + } } /* helo_response - respond to HELO command */ -static void helo_response(SINK_STATE *state) +static void helo_response(SINK_STATE *state, const char *args) { + /* HELO aborts a mail transaction in progress. */ + mail_cmd_reset(state); + state->client_proto = "SMTP"; smtp_printf(state->stream, "250 %s", var_myhostname); smtp_flush(state->stream); + if (single_template) { + if (state->helo_args) + myfree(state->helo_args); + SKIP(args, ISSPACE(*args)); + state->helo_args = mystrdup(args); + } } /* ok_response - send 250 OK */ -static void ok_response(SINK_STATE *state) +static void ok_response(SINK_STATE *state, const char *unused_args) { smtp_printf(state->stream, "250 2.0.0 Ok"); smtp_flush(state->stream); } -/* mail_response - reset recipient count, send 250 OK */ +/* rset_response - reset, send 250 OK */ -static void mail_response(SINK_STATE *state) +static void rset_response(SINK_STATE *state, const char *unused_args) { - state->rcpts = 0; + mail_cmd_reset(state); smtp_printf(state->stream, "250 2.1.0 Ok"); smtp_flush(state->stream); } +/* mail_response - reset recipient count, send 250 OK */ + +static void mail_response(SINK_STATE *state, const char *args) +{ + if (state->in_mail) { + smtp_printf(state->stream, "503 5.5.1 Error: nested MAIL command"); + smtp_flush(state->stream); + return; + } + state->in_mail++; + state->rcpts = 0; + smtp_printf(state->stream, "250 2.1.0 Ok"); + smtp_flush(state->stream); + if (single_template) { + mail_file_open(state); + SKIP(args, *args != ':'); + SKIP(args, *args == ':'); + SKIP(args, ISSPACE(*args)); + vstream_fprintf(state->dump_file, "X-Mail-Args: %s\n", args); + } +} + /* rcpt_response - bump recipient count, send 250 OK */ -static void rcpt_response(SINK_STATE *state) +static void rcpt_response(SINK_STATE *state, const char *args) { + if (state->in_mail == 0) { + smtp_printf(state->stream, "503 5.5.1 Error: need MAIL command"); + smtp_flush(state->stream); + return; + } state->rcpts++; smtp_printf(state->stream, "250 2.1.5 Ok"); smtp_flush(state->stream); + /* Note: there may be more than one recipient per mail transaction. */ + if (state->dump_file) { + SKIP(args, *args != ':'); + SKIP(args, *args == ':'); + SKIP(args, ISSPACE(*args)); + vstream_fprintf(state->dump_file, "X-Rcpt-Args: %s\n", args); + } } /* data_response - respond to DATA command */ -static void data_response(SINK_STATE *state) +static void data_response(SINK_STATE *state, const char *unused_args) { + if (state->in_mail == 0 || state->rcpts == 0) { + smtp_printf(state->stream, "503 5.5.1 Error: need RCPT command"); + smtp_flush(state->stream); + return; + } + /* Not: ST_ANY. */ state->data_state = ST_CR_LF; smtp_printf(state->stream, "354 End data with ."); smtp_flush(state->stream); state->read_fn = data_read; + if (state->dump_file) + mail_file_finish_header(state); } /* data_event - delayed response to DATA command */ @@ -294,7 +677,7 @@ static void data_event(int unused_event, char *context) { SINK_STATE *state = (SINK_STATE *) context; - data_response(state); + data_response(state, ""); } /* dot_resp_hard - hard error response to . command */ @@ -325,7 +708,7 @@ static void dot_resp_soft(SINK_STATE *state) /* dot_response - response to . command */ -static void dot_response(SINK_STATE *state) +static void dot_response(SINK_STATE *state, const char *unused_args) { if (enable_lmtp) { while (state->rcpts-- > 0) /* XXX this could block */ @@ -338,7 +721,7 @@ static void dot_response(SINK_STATE *state) /* quit_response - respond to QUIT command */ -static void quit_response(SINK_STATE *state) +static void quit_response(SINK_STATE *state, const char *unused_args) { smtp_printf(state->stream, "221 Bye"); smtp_flush(state->stream); @@ -348,7 +731,7 @@ static void quit_response(SINK_STATE *state) /* conn_response - respond to connect command */ -static void conn_response(SINK_STATE *state) +static void conn_response(SINK_STATE *state, const char *unused_args) { if (pretend_pix) smtp_printf(state->stream, "220 ********"); @@ -400,10 +783,19 @@ static int data_read(SINK_STATE *state) state->data_state = data_trans[0].next_state; else state->data_state = ST_ANY; + if (state->dump_file) { + if (ch != '\r' && state->data_state != ST_CR_LF_DOT) + VSTREAM_PUTC(ch, state->dump_file); + if (vstream_ferror(state->dump_file)) + msg_fatal("append file %s: %m", VSTREAM_PATH(state->dump_file)); + } if (state->data_state == ST_CR_LF_DOT_CR_LF) { PUSH_BACK_SET(state, ".\r\n"); state->read_fn = command_read; state->data_state = ST_ANY; + if (state->dump_file) + mail_file_finish(state); + mail_cmd_reset(state); if (count) { mesg_count++; do_stats(); @@ -427,7 +819,7 @@ static int data_read(SINK_STATE *state) */ typedef struct SINK_COMMAND { const char *name; - void (*response) (SINK_STATE *); + void (*response) (SINK_STATE *, const char *); void (*hard_response) (SINK_STATE *); void (*soft_response) (SINK_STATE *); int flags; @@ -451,7 +843,7 @@ static SINK_COMMAND command_table[] = { "rcpt", rcpt_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, "data", data_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, ".", dot_response, dot_resp_hard, dot_resp_soft, FLAG_ENABLE, - "rset", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, + "rset", rset_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, "noop", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, "vrfy", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, "quit", quit_response, hard_err_resp, soft_err_resp, FLAG_ENABLE, @@ -502,11 +894,12 @@ static void set_cmds_flags(const char *cmds, int flags) /* command_resp - respond to command */ -static int command_resp(SINK_STATE *state, SINK_COMMAND *cmdp, const char *command, char *args) +static int command_resp(SINK_STATE *state, SINK_COMMAND *cmdp, + const char *command, const char *args) { /* We use raw syslog. Sanitize data content and length. */ if (cmdp->flags & FLAG_SYSLOG) - syslog(LOG_INFO, "%s %.100s", command, printable(args, '?')); + syslog(LOG_INFO, "%s %.100s", command, args); if (cmdp->flags & FLAG_DISCONNECT) return (-1); if (cmdp->flags & FLAG_HARD_ERR) { @@ -520,7 +913,7 @@ static int command_resp(SINK_STATE *state, SINK_COMMAND *cmdp, const char *comma if (cmdp->response == data_response && fixed_delay > 0) { event_request_timer(data_event, (char *) state, fixed_delay); } else { - cmdp->response(state); + cmdp->response(state, args); if (cmdp->response == quit_response) return (-1); } @@ -604,7 +997,7 @@ static int command_read(SINK_STATE *state) */ vstring_truncate(state->buffer, VSTRING_LEN(state->buffer) - 2); VSTRING_TERMINATE(state->buffer); - state->data_state = ST_ANY; + state->data_state = ST_CR_LF; VSTRING_RESET(state->buffer); /* @@ -626,7 +1019,7 @@ static int command_read(SINK_STATE *state) smtp_flush(state->stream); return (0); } - return (command_resp(state, cmdp, command, ptr)); + return (command_resp(state, cmdp, command, printable(ptr, '?'))); } /* read_timeout - handle timer event */ @@ -698,6 +1091,8 @@ static void read_event(int unused_event, char *context) static void disconnect(SINK_STATE *state) { + static void connect_event(int, char *); + event_disable_readwrite(vstream_fileno(state->stream)); event_cancel_timer(read_timeout, (char *) state); if (count) { @@ -706,24 +1101,36 @@ static void disconnect(SINK_STATE *state) } vstream_fclose(state->stream); vstring_free(state->buffer); + /* Clean up file capture attributes. */ + if (state->helo_args) + myfree(state->helo_args); + /* Delete incomplete mail transaction. */ + mail_cmd_reset(state); myfree((char *) state); if (max_quit_count > 0 && quit_count >= max_quit_count) exit(0); + if (client_count-- == max_client_count) + event_enable_read(sock, connect_event, (char *) 0); } /* connect_event - handle connection events */ -static void connect_event(int unused_event, char *context) +static void connect_event(int unused_event, char *unused_context) { - int sock = CAST_CHAR_PTR_TO_INT(context); struct sockaddr sa; SOCKADDR_SIZE len = sizeof(sa); SINK_STATE *state; int fd; if ((fd = sane_accept(sock, &sa, &len)) >= 0) { + /* Safety: limit the number of open sockets and capture files. */ + if (++client_count == max_client_count) + event_disable_readwrite(sock); + state = (SINK_STATE *) mymalloc(sizeof(*state)); + SOCKADDR_TO_HOSTADDR(&sa, len, &state->client_addr, + (MAI_SERVPORT_STR *) 0, sa.sa_family); if (msg_verbose) - msg_info("connect (%s)", + msg_info("connect (%s %s)", #ifdef AF_LOCAL sa.sa_family == AF_LOCAL ? "AF_LOCAL" : #else @@ -733,15 +1140,24 @@ static void connect_event(int unused_event, char *context) #ifdef AF_INET6 sa.sa_family == AF_INET6 ? "AF_INET6" : #endif - "unknown protocol family"); + "unknown protocol family", + state->client_addr.buf); non_blocking(fd, NON_BLOCKING); - state = (SINK_STATE *) mymalloc(sizeof(*state)); state->stream = vstream_fdopen(fd, O_RDWR); state->buffer = vstring_alloc(1024); state->read_fn = command_read; state->data_state = ST_ANY; PUSH_BACK_SET(state, ""); smtp_timeout_setup(state->stream, var_tmout); + state->in_mail = 0; + state->rcpts = 0; + /* Initialize file capture attributes. */ + state->addr_prefix = (sa.sa_family == AF_INET6 ? "ipv6:" : ""); + state->helo_args = 0; + state->client_proto = enable_lmtp ? "LMTP" : "SMTP"; + state->start_time = 0; + state->id = 0; + state->dump_file = 0; /* * We use the smtp_stream module to produce output. That module @@ -780,12 +1196,11 @@ static void connect_event(int unused_event, char *context) static void usage(char *myname) { - msg_fatal("usage: %s [-468acCeEFLpPv] [-f commands] [-h hostname] [-n count] [-q commands] [-r commands] [-s commands] [-w delay] [host]:port backlog", myname); + msg_fatal("usage: %s [-468acCeEFLpPv] [-f commands] [-h hostname] [-m max_concurrency] [-n quit_count] [-q commands] [-r commands] [-s commands] [-w delay] [-d dump-template] [-D dump-template] [-S start-string] [host]:port backlog", myname); } int main(int argc, char **argv) { - int sock; int backlog; int ch; const char *protocols = INET_PROTO_NAME_ALL; @@ -804,7 +1219,7 @@ int main(int argc, char **argv) /* * Parse JCL. */ - while ((ch = GETOPT(argc, argv, "468acCeEf:Fh:Ln:pPq:r:s:t:vw:")) > 0) { + while ((ch = GETOPT(argc, argv, "468acCeEf:Fh:Ln:m:pPq:r:s:S:t:vw:d:D:")) > 0) { switch (ch) { case '4': protocols = INET_PROTO_NAME_IPV4; @@ -825,6 +1240,12 @@ int main(int argc, char **argv) disable_xclient = 1; reset_cmd_flags("xclient", FLAG_ENABLE); break; + case 'd': + single_template = optarg; + break; + case 'D': + shared_template = optarg; + break; case 'e': disable_esmtp = 1; break; @@ -845,9 +1266,13 @@ int main(int argc, char **argv) case 'L': enable_lmtp = 1; break; + case 'm': + if ((max_client_count = atoi(optarg)) <= 0) + msg_fatal("bad concurrency limit: %s", optarg); + break; case 'n': if ((max_quit_count = atoi(optarg)) <= 0) - msg_fatal("bad count: %s", optarg); + msg_fatal("bad quit count: %s", optarg); break; case 'p': disable_pipelining = 1; @@ -867,6 +1292,10 @@ int main(int argc, char **argv) openlog(basename(argv[0]), LOG_PID, LOG_MAIL); set_cmds_flags(optarg, FLAG_SYSLOG); break; + case 'S': + start_string = vstring_alloc(10); + unescape(start_string, optarg); + break; case 't': if ((var_tmout = atoi(optarg)) <= 0) msg_fatal("bad timeout: %s", optarg); @@ -886,6 +1315,8 @@ int main(int argc, char **argv) usage(argv[0]); if ((backlog = atoi(argv[optind + 1])) <= 0) usage(argv[0]); + if (single_template && shared_template) + msg_fatal("use only one of -d or -D, but not both"); /* * Initialize. @@ -904,10 +1335,15 @@ int main(int argc, char **argv) sock = inet_listen(argv[optind], backlog, BLOCKING); } + if (single_template) + mysrand((int) time((time_t *) 0)); + else if (shared_template) + single_template = shared_template; + /* * Start the event handler. */ - event_enable_read(sock, connect_event, CAST_INT_TO_CHAR_PTR(sock)); + event_enable_read(sock, connect_event, (char *) 0); for (;;) event_loop(-1); } diff --git a/postfix/src/smtpstone/smtp-source.c b/postfix/src/smtpstone/smtp-source.c index 5f84e4332..23f5e3b7e 100644 --- a/postfix/src/smtpstone/smtp-source.c +++ b/postfix/src/smtpstone/smtp-source.c @@ -17,6 +17,9 @@ /* Connections can be made to UNIX-domain and IPv4 or IPv6 servers. /* IPv4 and IPv6 are the default. /* +/* Note: this is an unsupported test program. No attempt is made +/* to maintain compatibility between successive versions. +/* /* Arguments: /* .IP \fB-4\fR /* Connect to the server with IPv4. This option has no effect when @@ -510,7 +513,7 @@ static void send_helo(SESSION *session) * Send the standard greeting with our hostname */ if ((except = vstream_setjmp(session->stream)) != 0) - msg_fatal("%s while sending HELO", exception_text(except)); + msg_fatal("%s while sending %s", exception_text(except), protocol); command(session->stream, "%s %s", protocol, var_myhostname); @@ -528,15 +531,16 @@ static void helo_done(int unused_event, char *context) SESSION *session = (SESSION *) context; RESPONSE *resp; int except; + const char *protocol = (talk_lmtp ? "LHLO" : "HELO"); /* * Get response to HELO command. */ if ((except = vstream_setjmp(session->stream)) != 0) - msg_fatal("%s while sending HELO", exception_text(except)); + msg_fatal("%s while sending %s", exception_text(except), protocol); if ((resp = response(session->stream, buffer))->code / 100 != 2) - msg_fatal("HELO rejected: %d %s", resp->code, resp->str); + msg_fatal("%s rejected: %d %s", protocol, resp->code, resp->str); send_mail(session); } diff --git a/postfix/src/util/peekfd.c b/postfix/src/util/peekfd.c index 9e984908a..f142f262a 100644 --- a/postfix/src/util/peekfd.c +++ b/postfix/src/util/peekfd.c @@ -15,6 +15,10 @@ /* DIAGNOSTICS /* peekfd() returns -1 in case of trouble. The global \fIerrno\fR /* variable reflects the nature of the problem. +/* BUGS +/* On some systems, non-blocking read() may fail even after a +/* positive return from peekfd(). The smtp-sink program works +/* around this by using the readable() function instead. /* LICENSE /* .ad /* .fi diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index d71f3589e..ed857b3e3 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -1049,8 +1049,9 @@ int vstream_fclose(VSTREAM *stream) */ if (stream->pid != 0) msg_panic("vstream_fclose: stream has process"); - if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0) + if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0 && stream->fd >= 0) vstream_fflush(stream); + /* Do not remove: vstream_fdclose() depends on this error test. */ err = vstream_ferror(stream); if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { if (stream->read_fd >= 0) @@ -1079,8 +1080,22 @@ int vstream_fclose(VSTREAM *stream) int vstream_fdclose(VSTREAM *stream) { + + /* + * Flush unwritten output, just like vstream_fclose(). Errors are + * reported by vstream_fclose(). + */ + if ((stream->buf.flags & VSTREAM_FLAG_WRITE_DOUBLE) != 0) + (void) vstream_fflush(stream); + + /* + * NOTE: Negative file descriptors are not part of the external + * interface. They are for internal use only, in order to support + * vstream_fdclose() without a lot of code duplication. Applications that + * rely on negative VSTREAM file descriptors will break without warning. + */ if (stream->buf.flags & VSTREAM_FLAG_DOUBLE) { - stream->read_fd = stream->write_fd = -1; + stream->fd = stream->read_fd = stream->write_fd = -1; } else { stream->fd = -1; }