]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.4-20060823
authorWietse Venema <wietse@porcupine.org>
Wed, 23 Aug 2006 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:32:33 +0000 (06:32 +0000)
36 files changed:
postfix/HISTORY
postfix/README_FILES/CDB_README
postfix/README_FILES/SASL_README
postfix/auxiliary/name-addr-test/getaddrinfo.c [new file with mode: 0644]
postfix/auxiliary/name-addr-test/gethostbyaddr.c [new file with mode: 0644]
postfix/auxiliary/name-addr-test/gethostbyname.c [new file with mode: 0644]
postfix/auxiliary/name-addr-test/getnameinfo.c [new file with mode: 0644]
postfix/conf/post-install
postfix/html/CDB_README.html
postfix/html/SASL_README.html
postfix/html/master.5.html
postfix/html/postconf.5.html
postfix/html/qmqp-sink.1.html
postfix/html/qmqp-source.1.html
postfix/html/smtp-sink.1.html
postfix/html/smtp-source.1.html
postfix/man/man1/qmqp-sink.1
postfix/man/man1/qmqp-source.1
postfix/man/man1/smtp-sink.1
postfix/man/man1/smtp-source.1
postfix/man/man5/master.5
postfix/man/man5/postconf.5
postfix/proto/CDB_README.html
postfix/proto/SASL_README.html
postfix/proto/master
postfix/proto/postconf.proto
postfix/src/cleanup/cleanup_milter.c
postfix/src/global/mail_date.c
postfix/src/global/mail_version.h
postfix/src/smtpd/smtpd.c
postfix/src/smtpstone/qmqp-sink.c
postfix/src/smtpstone/qmqp-source.c
postfix/src/smtpstone/smtp-sink.c
postfix/src/smtpstone/smtp-source.c
postfix/src/util/peekfd.c
postfix/src/util/vstream.c

index 5856ca152895d6d11a1d3649d7c5c30d7e9c24c2..100cb1ec3e8b236b00911e1cc81dfda582d1f542 100644 (file)
@@ -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
index 0d00bf59ac8519d4dd076889ab5b930eab8272d3..b93993291a3c14629eee07055441855ae0da9660 100644 (file)
@@ -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
 "p\bpo\bos\bst\btm\bma\bap\bp -\b-i\bi" (incremental record insertion) and "p\bpo\bos\bst\btm\bma\bap\bp -\b-d\bd" (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.
 
index 87ae46b270523070597500c77340b48900300fed..5cf940db4c705f249616a743cabacf13d18651d5 100644 (file)
@@ -61,24 +61,23 @@ Needless to say, these commands are not available in earlier Postfix versions.
 
 B\bBu\bui\bil\bld\bdi\bin\bng\bg P\bPo\bos\bst\btf\bfi\bix\bx w\bwi\bit\bth\bh D\bDo\bov\bve\bec\bco\bot\bt S\bSA\bAS\bSL\bL s\bsu\bup\bpp\bpo\bor\brt\bt
 
-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 (file)
index 0000000..df551f2
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+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 (file)
index 0000000..e58db9e
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+
+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 (file)
index 0000000..d8079dd
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+
+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 (file)
index 0000000..a270a06
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+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);
+}
index 1d919ccfed20de35dc937c4c4ffb85c2e631f736..c64ff19f6164d0a5791d2b0f343098f7942f3c5c 100644 (file)
@@ -643,6 +643,7 @@ EOF
            ed $config_directory/master.cf <<EOF || exit 1
 /^tlsmgr[      ]*fifo[         ]/
 s/fifo/unix/
+s/[0-9][0-9]*/&?/
 p
 w
 q
index 9d81ee2f7a5f960f9dd7aa0cd9cc14a5b92d59de..a7e51350a4a2e70a1238196dbbe1cb2f9026b332 100644 (file)
@@ -79,10 +79,11 @@ like: </p>
 </pre>
 </blockquote>
 
-<p> After postfix has been built with cdb support, you can use
+<p> 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 "<b>postmap -i</b>" (incremental record
 insertion) and "<b>postmap -d</b>" (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 <a href="verify.8.html">verify(8)</a> service. </p>
+verification cache for the <a href="verify.8.html">verify(8)</a> service, or to store
+TLS session information for the <a href="tlsmgr.8.html">tlsmgr(8)</a> service. </p>
index 3086d14e4cc804d748f848dbdabf6ad24d41c4a1..20b1a562f1e30076346d06ee27781919f048f964 100644 (file)
@@ -113,9 +113,8 @@ Postfix versions. </p>
 <h2><a name="build_dovecot">Building Postfix with Dovecot SASL
 support</a></h2>
 
-<p> Dovecot SASL support is available in Postfix 2.3 and later. The
-Dovecot source code is available via <a href="http://www.dovecot.org/">http://www.dovecot.org/</a>. At
-the time
+<p> 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: </p>
 
 <blockquote>
 <pre>
-% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SASL_SERVER_TYPE=\"dovecot\"'
+% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SERVER_SASL_TYPE=\"dovecot\"'
 </pre>
 </blockquote>
 
@@ -138,7 +137,7 @@ in the Postfix top-level directory: </p>
 
 <ul>
 
-<li> <p> The "-DDEF_SASL_SERVER_TYPE" stuff is not necessary; it just
+<li> <p> 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 <a href="postconf.5.html">main.cf</a>
 file.  </p>
index 06b0f6238eddd74d813cc77b30c7ba897a6a0e23..5bddcf7c3e0d6a3705f0bd14106056b4a234f458 100644 (file)
@@ -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 <a href="pickup.8.html"><b>pickup</b>(8)</a>, <a href="qmgr.8.html"><b>qmgr</b>(8)</a> and <a href="flush.8.html"><b>flush</b>(8)</a> daemons require
               a wake up timer.
index b485dc1d8b7cc706e45442e6dc88d9f992e07628..3b2d15490500e1800b3ed25ebfbfb8df906754cf 100644 (file)
@@ -5508,6 +5508,11 @@ may wish to turn on the policy (UCE and mail relaying) and protocol
 error (broken mail software) reports.
 </p>
 
+<p> 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.
+</p>
+
 <p>
 The error classes are:
 </p>
@@ -11823,7 +11828,8 @@ message contains no To: or Cc: message header.  </p>
 <p>
 The numerical Postfix SMTP server response code when a sender or
 recipient address is rejected by the <a href="postconf.5.html#reject_unknown_sender_domain">reject_unknown_sender_domain</a>
-or <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a> restriction.
+or <a href="postconf.5.html#reject_unknown_recipient_domain">reject_unknown_recipient_domain</a> restriction.  The response is
+always 450 in case of a temporary DNS error.
 </p>
 
 <p>
index 150462ffb2acb3594fd5de96d87da942fbc39b8f..5811a80668dc8fea76a0f3b12b9b617f72f1a415 100644 (file)
@@ -23,6 +23,12 @@ QMQP-SINK(1)                                                      QMQP-SINK(1)
        and IPv6 are the default.  This program is the  complement
        of the <a href="qmqp-source.1.html"><b>qmqp-source</b>(1)</a> program.
 
+       Note:  this  is an unsupported test program. No attempt is
+       made to maintain  compatibility  between  successive  ver-
+       sions.
+
+       Arguments:
+
        <b>-4</b>     Support  IPv4  only. This option has no effect when
               Postfix is built without IPv6 support.
 
index 27e8fa47bf03614c8363dbe04abe14041908348b..652741b7b49280cb2807cd99e359277784efd932 100644 (file)
@@ -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:
 
        <b>-4</b>     Connect to the server with IPv4. This option has no
               effect when Postfix is built without IPv6  support.
index dcd9623de9df3b305176afe6bf3515d6bc10359b..1880535a918e3b3da2e7ec7b7ee509f396f0b92c 100644 (file)
@@ -20,11 +20,21 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
        away.   The  purpose is to measure client performance, not
        protocol compliance.
 
+       <b>smtp-sink</b> 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 <a href="smtp-source.1.html"><b>smtp-source</b>(1)</a>  pro-
        gram.
 
+       Note:  this  is an unsupported test program. No attempt is
+       made to maintain  compatibility  between  successive  ver-
+       sions.
+
        Arguments:
 
        <b>-4</b>     Support  IPv4  only. This option has no effect when
@@ -43,6 +53,30 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
 
        <b>-C</b>     Disable XCLIENT support.
 
+       <b>-d</b> <i>dump-template</i>
+              Dump each mail transaction to a single-message file
+              whose name is created by  expanding  the  <i>dump-tem-</i>
+              <i>plate</i> 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.
+
+       <b>-D</b> <i>dump-template</i>
+              Append  mail  transactions  to a multi-message dump
+              file whose name is created by expanding  the  <i>dump-</i>
+              <i>template</i> 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.
+
        <b>-e</b>     Do not announce ESMTP support.
 
        <b>-E</b>     Do not announce ENHANCEDSTATUSCODES support.
@@ -66,6 +100,13 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
 
        <b>-L</b>     Enable LMTP instead of SMTP.
 
+       <b>-m</b> <i>count</i> (default: 256)
+              An upper bound on the maximal number of  simultane-
+              ous  connections  that  <b>smtp-sink</b> will handle. This
+              prevents the  process  from  running  out  of  file
+              descriptors. Excess connections will stay queued in
+              the TCP/IP stack.
+
        <b>-n</b> <i>count</i>
               Terminate after <i>count</i> 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.
 
+       <b>-S start-string</b>
+              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),  \<i>ddd</i>  (up to three octal digits) and \\ (the
+              backslash character).
+
        <b>-t</b> <i>timeout</i> (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.
 
        <b>-v</b>     Show the SMTP conversations.
 
@@ -116,7 +167,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               mand.
 
        [<b>inet:</b>][<i>host</i>]:<i>port</i>
-              Listen on  network  interface  <i>host</i>  (default:  any
+              Listen  on  network  interface  <i>host</i>  (default: any
               interface) TCP port <i>port</i>. Both <i>host</i> and <i>port</i> 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 <i>pathname</i>.
 
        <i>backlog</i>
-              The maximum length the  queue  of  pending  connec-
+              The  maximum  length  the  queue of pending connec-
               tions, as defined by the <b>listen</b>(2) system call.
 
+<b>DUMP FILE FORMAT</b>
+       Each dumped message contains a  sequence  of  text  lines,
+       terminated  with  the  newline  character. The sequence of
+       information is as follows:
+
+       <b>o</b>      The optional string specified with the <b>-S</b> option.
+
+       <b>o</b>      The  <b>smtp-sink</b>  generated  headers  as   documented
+              below.
+
+       <b>o</b>      The  message  header  and body as received from the
+              SMTP client.
+
+       <b>o</b>      An empty line.
+
+       The format of the <b>smtp-sink</b> generated headers is  as  fol-
+       lows:
+
+       <b>X-Client-Addr:</b> <i>text</i>
+              The client IP address without enclosing []. An IPv6
+              address is prefixed with "ipv6:".  This  record  is
+              always present.
+
+       <b>X-Client-Proto:</b> <i>text</i>
+              The  client  protocol:  SMTP,  ESMTP  or LMTP. This
+              record is always present.
+
+       <b>X-Helo-Args:</b> <i>text</i>
+              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.
+
+       <b>X-Mail-Args:</b> <i>text</i>
+              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.
+
+       <b>X-Rcpt-Args:</b> <i>text</i>
+              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.
+
+       <b>Received:</b> <i>text</i>
+              A message header for compatibility with  mail  pro-
+              cessing  software. This three-line header marks the
+              end of the headers provided by  <b>smtp-sink</b>,  and  is
+              formatted as follows:
+
+              <b>from</b> <i>helo</i> <b>([</b><i>addr</i><b>])</b>
+                     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.
+
+              <b>by</b> <i>host</i> <b>(smtp-sink) with</b> <i>proto</i> <b>id</b> <i>random</i><b>;</b>
+                     The hostname specified with the  <b>-h</b>  option,
+                     the   client  protocol  (see  <b>X-Client-Proto</b>
+                     above), and the pseudo-random portion of the
+                     per-message capture file name.
+
+              <i>time-stamp</i>
+                     A time stamp as defined in <a href="http://www.faqs.org/rfcs/rfc2822.html">RFC 2822</a>.
+
 <b>SEE ALSO</b>
        <a href="smtp-source.1.html">smtp-source(1)</a>, SMTP/LMTP message generator
 
index 19e882d661523c3e6eeb3dfdee95917cb32077bb..31f285055b89d84be314d491cabdaca83d9155c7 100644 (file)
@@ -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:
 
        <b>-4</b>     Connect to the server with IPv4. This option has no
index 07f6ad0e522974e2e4374e8bcb0e8aba6c3f5ce7..f26c1c9c2c414262e15429c8a15b4d4c94362520 100644 (file)
@@ -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.
index 668c595a8b74dd5a1ffbf092ce35d14b71ecd886..9cdfb476c10ff5b6513c9d06741b54bde27a40fe 100644 (file)
@@ -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.
index 6a792585025507cf00bd00c6bc00732e6fc54a05..dff7310b2ddd08f1f0296b89d171ed310bf640dd 100644 (file)
@@ -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
index 594363cf7d84645a63096115e5179501bb423f04..0dabccf98f1621259105c4c08c8887314dddc8e0 100644 (file)
@@ -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
index 012b1331a4d70b9139192ffa733cfc585c480aa6..632a5af0feec06fe344b414d5fa0ab9f52234a88 100644 (file)
@@ -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)
index 8484d021615c0985f29c2c0d93f6024ffc676085..c37d800ba583358cebb5cc6c1e82d9f6d850dc33 100644 (file)
@@ -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)
index fd56bdc2ef03ffa7965babcf97429d0b8d7e924f..a025cd3e5c7ad1008e5464953f877c02015b4f6d 100644 (file)
@@ -79,10 +79,11 @@ like: </p>
 </pre>
 </blockquote>
 
-<p> After postfix has been built with cdb support, you can use
+<p> 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 "<b>postmap -i</b>" (incremental record
 insertion) and "<b>postmap -d</b>" (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. </p>
+verification cache for the verify(8) service, or to store
+TLS session information for the tlsmgr(8) service. </p>
index cd6d23ab1455964909f74c883258c39ad2570da3..b9c662aaf95fcbb3f1d8b9cedfc43b4ea12c5f6e 100644 (file)
@@ -113,9 +113,8 @@ Postfix versions. </p>
 <h2><a name="build_dovecot">Building Postfix with Dovecot SASL
 support</a></h2>
 
-<p> 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
+<p> 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: </p>
 
 <blockquote>
 <pre>
-% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SASL_SERVER_TYPE=\"dovecot\"'
+% make makefiles CCARGS='-DUSE_SASL_AUTH -DDEF_SERVER_SASL_TYPE=\"dovecot\"'
 </pre>
 </blockquote>
 
@@ -138,7 +137,7 @@ INSTALL document. </p>
 
 <ul>
 
-<li> <p> The "-DDEF_SASL_SERVER_TYPE" stuff is not necessary; it just
+<li> <p> 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.  </p>
index 4720caa8abb4d33a27fe24edad9eb0502c66b240..57674b13d28ec1d4cdf0d286a69b4879c475da2d 100644 (file)
 #      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)
index 0918f8db92191d4c0b514c12dae9ed05aa28c07c..f031b6f571687c9207989689fd93668c03bf529f 100644 (file)
@@ -2801,6 +2801,11 @@ may wish to turn on the policy (UCE and mail relaying) and protocol
 error (broken mail software) reports.
 </p>
 
+<p> 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.
+</p>
+
 <p>
 The error classes are:
 </p>
@@ -5894,7 +5899,8 @@ The default time unit is s (seconds).
 <p>
 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.
 </p>
 
 <p>
index e4acad297c8be993adf11427d1518c43186e6f37..c01d56ef18d9bc8c515bda0433cddd6d94837853 100644 (file)
 
 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 */
index 93edfe9c20e47f3273789c1cdd0d0280ee305aec..55d8907766e40aeca7e84e0e35c7046e4cf9358c 100644 (file)
@@ -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));
index ed34b8c06cd48e193194da722f1839ee6f03c93c..49391792244f9d1abe6c47305a3138bd7edcdc16 100644 (file)
@@ -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
index 8dcc789b497d45ba85f2593482a2671f827d51fc..7447a2cff9ebdc947f1254ce7b7ace459b3faf1c 100644 (file)
@@ -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;
index 2116b1bda132b44268f03e29170e19e6266a2062..30390e6275b3712598fdf1897a555f739706a487 100644 (file)
 /*     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.
index d06054215edbf2539c000fd02fd540116f69ae74..2606e6e118bfa4c07bbf5584e3ea6614df0589a5 100644 (file)
 /*     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.
index 289c2f1a388890d75278f062f112f55799d037e6..dc151c882f78fb4bbd34d5fa3e2e6d4c84e08df1 100644 (file)
 /*     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
 /*     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
 /*     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
 /*     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.
 /* .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
 #include <sys_defs.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <syslog.h>
 #include <signal.h>
+#include <time.h>
+#include <ctype.h>
 
 #ifdef STRCASECMP_IN_STRINGS_H
 #include <strings.h>
 #include <stringops.h>
 #include <sane_accept.h>
 #include <inet_proto.h>
+#include <myaddrinfo.h>
+#include <make_dirs.h>
+#include <myrand.h>
 
 /* Global library. */
 
 #include <smtp_stream.h>
+#include <mail_date.h>
 
 /* 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);
 }
 
+/* rset_response - reset, send 250 OK */
+
+static void rset_response(SINK_STATE *state, const char *unused_args)
+{
+    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)
+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 <CR><LF>.<CR><LF>");
     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);
 }
index 5f84e43320189ea553129ceb33661519f1af98ad..23f5e3b7e0677683a3455e36fa04b7d91af4929d 100644 (file)
@@ -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);
 }
index 9e984908a5a59a51ef80b611f899b9e56f8891ac..f142f262aa785cee0f9e363243e050ba63c5d119 100644 (file)
 /* 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
index d71f3589e72a7064c399844ceab5bfcfe172c4d1..ed857b3e3a5fed7c2b2952a272594571e0786b30 100644 (file)
@@ -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;
     }